changes to threads; --recursivePath support

This commit is contained in:
Araq
2011-01-29 01:47:58 +01:00
parent 557adbcaac
commit f46870fe1c
20 changed files with 396 additions and 180 deletions

View File

@@ -30,6 +30,7 @@ path="$lib/windows"
path="$lib/posix"
path="$lib/ecmas"
path="$lib/pure/unidecode"
#recursivePath:"$user/.babel/lib"
@if release or quick:
obj_checks:off

View File

@@ -22,6 +22,7 @@ Advanced options:
--os:SYMBOL set the target operating system (cross-compilation)
--cpu:SYMBOL set the target processor (cross-compilation)
--debuginfo enables debug information
--debugger:on|off turn Embedded Nimrod Debugger on|off
-t, --passc:OPTION pass an option to the C compiler
-l, --passl:OPTION pass an option to the linker
--genMapping generate a mapping file containing

View File

@@ -18,7 +18,7 @@ Options:
-f, --forceBuild force rebuilding of all modules
--stackTrace:on|off turn stack tracing on|off
--lineTrace:on|off turn line tracing on|off
--debugger:on|off turn Embedded Nimrod Debugger on|off
--threads:on|off turn support for multi-threading on|off
-x, --checks:on|off turn all runtime checks on|off
--objChecks:on|off turn obj conversion checks on|off
--fieldChecks:on|off turn case variant field checks on|off

View File

@@ -16,11 +16,11 @@
## .. code-block:: nimrod
##
## var
## thr: array [0..4, TThread]
## thr: array [0..4, TThread[tuple[a,b: int]]]
## L: TLock
##
## proc threadFunc(c: pointer) {.procvar.} =
## for i in 0..9:
## proc threadFunc(interval: tuple[a,b: int]) {.procvar.} =
## for i in interval.a..interval.b:
## Aquire(L) # lock stdout
## echo i
## Release(L)
@@ -28,10 +28,12 @@
## InitLock(L)
##
## for i in 0..high(thr):
## createThread(thr[i], threadFunc)
## createThread(thr[i], threadFunc, (i*10, i*10+5))
## for i in 0..high(thr):
## joinThread(thr[i])
when not defined(boehmgc) and not defined(nogc):
{.error: "Thread support requires --gc:boehm or --gc:none".}
# We jump through some hops here to ensure that Nimrod thread procs can have
# the Nimrod calling convention. This is needed because thread procs are
@@ -41,10 +43,9 @@
# GC'ed closures in Nimrod.
type
TThreadProc* = proc (closure: pointer) ## Standard Nimrod thread proc.
TThreadProcClosure {.pure, final.} = object
fn: TThreadProc
data: pointer
TThreadProcClosure {.pure, final.}[TParam] = object
fn: proc (p: TParam)
data: TParam
when defined(Windows):
type
@@ -60,17 +61,22 @@ when defined(Windows):
TWinThreadProc = proc (x: pointer): int32 {.stdcall.}
TLock* = TSysLock ## Standard Nimrod Lock type.
proc InitLock*(L: var TLock) {.stdcall,
proc InitSysLock(L: var TSysLock) {.stdcall,
dynlib: "kernel32", importc: "InitializeCriticalSection".}
## Initializes the lock `L`.
proc Aquire*(L: var TLock) {.stdcall,
proc TryAquireSysAux(L: var TSysLock): int32 {.stdcall,
dynlib: "kernel32", importc: "TryEnterCriticalSection".}
## Tries to aquire the lock `L`.
proc TryAquireSys(L: var TSysLock): bool {.inline.} =
result = TryAquireSysAux(L) != 0'i32
proc AquireSys(L: var TSysLock) {.stdcall,
dynlib: "kernel32", importc: "EnterCriticalSection".}
## Aquires the lock `L`.
proc Release*(L: var TLock) {.stdcall,
proc ReleaseSys(L: var TSysLock) {.stdcall,
dynlib: "kernel32", importc: "LeaveCriticalSection".}
## Releases the lock `L`.
@@ -99,8 +105,8 @@ when defined(Windows):
proc TerminateThread(hThread: THandle, dwExitCode: int32): int32 {.
stdcall, dynlib: "kernel32", importc: "TerminateThread".}
proc threadProcWrapper(closure: pointer): int32 {.stdcall.} =
var c = cast[ptr TThreadProcClosure](closure)
proc threadProcWrapper[TParam](closure: pointer): int32 {.stdcall.} =
var c = cast[ptr TThreadProcClosure[TParam]](closure)
c.fn(c.data)
# implicitely return 0
@@ -109,16 +115,18 @@ else:
TSysLock {.importc: "pthread_mutex_t", header: "<sys/types.h>".} = int
TSysThread {.importc: "pthread_t", header: "<sys/types.h>".} = int
TLock* = TSysLock
proc InitLockAux(L: var TSysLock, attr: pointer = nil) {.
proc InitSysLock(L: var TSysLock, attr: pointer = nil) {.
importc: "pthread_mutex_init", header: "<pthread.h>".}
proc InitLock*(L: var TLock) {.inline.} =
InitLockAux(L)
proc Aquire*(L: var TLock) {.
proc AquireSys(L: var TSysLock) {.
importc: "pthread_mutex_lock", header: "<pthread.h>".}
proc Release*(L: var TLock) {.
proc TryAquireSysAux(L: var TSysLock): cint {.
importc: "pthread_mutex_trylock", header: "<pthread.h>".}
proc TryAquireSys(L: var TSysLock): bool {.inline.} =
result = TryAquireSysAux(L) == 0'i32
proc ReleaseSys(L: var TSysLock) {.
importc: "pthread_mutex_unlock", header: "<pthread.h>".}
proc pthread_create(a1: var TSysThread, a2: ptr int,
@@ -131,44 +139,205 @@ else:
proc pthread_cancel(a1: TSysThread): cint {.
importc: "pthread_cancel", header: "<pthread.h>".}
proc threadProcWrapper(closure: pointer) {.noconv.} =
var c = cast[ptr TThreadProcClosure](closure)
proc threadProcWrapper[TParam](closure: pointer) {.noconv.} =
var c = cast[ptr TThreadProcClosure[TParam]](closure)
c.fn(c.data)
{.passL: "-pthread".}
{.passC: "-pthread".}
type
TThread* = object of TObject ## Nimrod thread.
sys: TSysThread
c: TThreadProcClosure
const
noDeadlocks = true # compileOption("deadlockPrevention")
proc createThread*(t: var TThread, tp: TThreadProc,
closure: pointer = nil) =
## creates a new thread `t` and starts its execution. Entry point is the
## proc `tp`. `closure` is passed to `tp`.
t.c.data = closure
t.c.fn = tp
when defined(windows):
var dummyThreadId: int32
t.sys = CreateThread(nil, 0'i32, threadProcWrapper, addr(t.c), 0'i32,
dummyThreadId)
else:
discard pthread_create(t.sys, nil, threadProcWrapper, addr(t.c))
when noDeadLocks:
type
TLock* {.pure, final.} = object ## Standard Nimrod Lock type.
key: int # used for identity and global order!
sys: TSysLock
next: ptr TLock
else:
type
TLock* = TSysLock
type
TThread* {.pure, final.}[TParam] = object ## Nimrod thread.
sys: TSysThread
c: TThreadProcClosure[TParam]
proc joinThread*(t: TThread) =
when nodeadlocks:
var
lockList {.threadvar.}: ptr TLock
deadlocksPrevented* = 0 ## counts the number of times a
## deadlock has been prevented
proc InitLock*(L: var TLock) {.inline.} =
## Initializes the lock `L`.
when noDeadlocks:
InitSysLock(L.sys)
L.key = cast[int](addr(L))
else:
InitSysLock(L)
proc TryAquire*(L: var TLock): bool {.inline.} =
## Try to aquires the lock `L`. Returns `true` on success.
when noDeadlocks:
result = TryAquireSys(L.sys)
else:
result = TryAquireSys(L)
proc Aquire*(L: var TLock) =
## Aquires the lock `L`.
when nodeadlocks:
# Note: we MUST NOT change the linked list of locks before we have aquired
# the proper locks! This is because the pointer to the next lock is part
# of the lock itself!
assert L.key != 0
var p = lockList
if p == nil:
# simple case: no lock aquired yet:
AquireSys(L.sys)
locklist = addr(L)
L.next = nil
else:
# check where to put L into the list:
var r = p
var last: ptr TLock = nil
while L.key < r.key:
if r.next == nil:
# best case: L needs to be aquired as last lock, so we can
# skip a good amount of work:
AquireSys(L.sys)
r.next = addr(L)
L.next = nil
return
last = r
r = r.next
# special case: thread already holds L!
if L.key == r.key: return
# bad case: L needs to be somewhere in between
# release all locks after L:
var rollback = r
while r != nil:
ReleaseSys(r.sys)
r = r.next
# and aquire them in the correct order again:
AquireSys(L.sys)
r = rollback
while r != nil:
assert r.key < L.key
AquireSys(r.sys)
r = r.next
# now that we have all the locks we need, we can insert L
# into our list:
if last != nil:
L.next = last.next
last.next = addr(L)
else:
L.next = lockList
lockList = addr(L)
inc(deadlocksPrevented)
else:
AquireSys(L)
proc Release*(L: var TLock) =
## Releases the lock `L`.
when nodeadlocks:
assert L.key != 0
var p = lockList
var last: ptr TLock = nil
while true:
# if we don't find the lock, die by reading from nil!
if p.key == L.key:
if last != nil:
last.next = p.next
else:
assert p == lockList
lockList = locklist.next
L.next = nil
break
last = p
p = p.next
ReleaseSys(L.sys)
else:
ReleaseSys(L)
proc joinThread*[TParam](t: TThread[TParam]) {.inline.} =
## waits for the thread `t` until it has terminated.
when defined(windows):
when hostOS == "windows":
discard WaitForSingleObject(t.sys, -1'i32)
else:
discard pthread_join(t.sys, nil)
proc destroyThread*(t: var TThread) =
proc destroyThread*[TParam](t: var TThread[TParam]) {.inline.} =
## forces the thread `t` to terminate. This is potentially dangerous if
## you don't have full control over `t` and its aquired ressources.
when defined(windows):
## you don't have full control over `t` and its aquired resources.
when hostOS == "windows":
discard TerminateThread(t.sys, 1'i32)
else:
discard pthread_cancel(t.sys)
proc createThread*[TParam](t: var TThread[TParam],
tp: proc (param: TParam),
param: TParam) =
## creates a new thread `t` and starts its execution. Entry point is the
## proc `tp`. `param` is passed to `tp`.
t.c.data = param
t.c.fn = tp
when hostOS == "windows":
var dummyThreadId: int32
t.sys = CreateThread(nil, 0'i32, threadProcWrapper[TParam],
addr(t.c), 0'i32, dummyThreadId)
else:
discard pthread_create(t.sys, nil, threadProcWrapper[TParam], addr(t.c))
when isMainModule:
var
thr: array [0..4, TThread[tuple[a,b: int]]]
L, M, N: TLock
proc threadFunc(interval: tuple[a,b: int]) {.procvar.} =
for i in interval.a..interval.b:
case i mod 6
of 0:
Aquire(L) # lock stdout
Aquire(M)
Aquire(N)
of 1:
Aquire(L)
Aquire(N) # lock stdout
Aquire(M)
of 2:
Aquire(M)
Aquire(L)
Aquire(N)
of 3:
Aquire(M)
Aquire(N)
Aquire(L)
of 4:
Aquire(N)
Aquire(M)
Aquire(L)
of 5:
Aquire(N)
Aquire(L)
Aquire(M)
else: assert false
echo i
echo "deadlocks prevented: ", deadlocksPrevented
Release(L)
Release(M)
Release(N)
InitLock(L)
InitLock(M)
InitLock(N)
for i in 0..high(thr):
createThread(thr[i], threadFunc, (i*100, i*100+50))
for i in 0..high(thr):
joinThread(thr[i])

View File

@@ -1,7 +1,7 @@
#
#
# Nimrod's Runtime Library
# (c) Copyright 2010 Andreas Rumpf
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -24,3 +24,27 @@ proc nimLoadLibraryError(path: string) {.compilerproc, noinline.}
proc setStackBottom(theStackBottom: pointer) {.compilerRtl, noinline.}
# Support for thread local storage:
when false:
when defined(windows):
proc TlsAlloc(): int32 {.importc: "TlsAlloc", stdcall, dynlib: "kernel32".}
proc TlsSetValue(dwTlsIndex: int32, lpTlsValue: pointer) {.
importc: "TlsSetValue", stdcall, dynlib: "kernel32".}
proc TlsGetValue(dwTlsIndex: int32): pointer {.
importc: "TlsGetValue", stdcall, dynlib: "kernel32".}
else:
type
Tpthread_key {.importc: "pthread_key_t", header: "<sys/types.h>".} = int
proc pthread_getspecific(a1: Tpthread_key): pointer {.
importc: "pthread_getspecific", header: "<pthread.h>".}
proc pthread_key_create(a1: ptr Tpthread_key,
a2: proc (x: pointer) {.noconv.}): int32 {.
importc: "pthread_key_create", header: "<pthread.h>".}
proc pthread_key_delete(a1: Tpthread_key): int32 {.
importc: "pthread_key_delete", header: "<pthread.h>".}
proc pthread_setspecific(a1: Tpthread_key, a2: pointer): int32 {.
importc: "pthread_setspecific", header: "<pthread.h>".}

View File

@@ -380,14 +380,20 @@ proc assignLocalVar(p: BProc, s: PSym) =
appf(p.s[cpsLocals], " $1;$n", [s.loc.r])
localDebugInfo(p, s)
proc declareThreadVar(m: BModule, s: PSym) =
if optThreads in gGlobalOptions:
app(m.s[cfsVars], "NIM_THREADVAR ")
app(m.s[cfsVars], getTypeDesc(m, s.loc.t))
proc assignGlobalVar(p: BProc, s: PSym) =
if s.loc.k == locNone:
fillLoc(s.loc, locGlobalVar, s.typ, mangleName(s), OnHeap)
useHeader(p.module, s)
if lfNoDecl in s.loc.flags: return
if sfImportc in s.flags: app(p.module.s[cfsVars], "extern ")
if sfThreadVar in s.flags: app(p.module.s[cfsVars], "NIM_THREADVAR ")
app(p.module.s[cfsVars], getTypeDesc(p.module, s.loc.t))
if sfThreadVar in s.flags: declareThreadVar(p.module, s)
else: app(p.module.s[cfsVars], getTypeDesc(p.module, s.loc.t))
if sfRegister in s.flags: app(p.module.s[cfsVars], " register")
if sfVolatile in s.flags: app(p.module.s[cfsVars], " volatile")
appf(p.module.s[cfsVars], " $1;$n", [s.loc.r])
@@ -686,8 +692,8 @@ proc genVarPrototype(m: BModule, sym: PSym) =
[sym.loc.r, getTypeDesc(m, sym.loc.t)])
else:
app(m.s[cfsVars], "extern ")
if sfThreadVar in sym.flags: app(m.s[cfsVars], "NIM_THREADVAR ")
app(m.s[cfsVars], getTypeDesc(m, sym.loc.t))
if sfThreadVar in sym.flags: declareThreadVar(m, sym)
else: app(m.s[cfsVars], getTypeDesc(m, sym.loc.t))
if sfRegister in sym.flags: app(m.s[cfsVars], " register")
if sfVolatile in sym.flags: app(m.s[cfsVars], " volatile")
appf(m.s[cfsVars], " $1;$n", [sym.loc.r])

View File

@@ -25,7 +25,7 @@ proc ProcessCommand*(switch: string, pass: TCmdLinePass)
proc processSwitch*(switch, arg: string, pass: TCmdlinePass, info: TLineInfo)
# implementation
const
const
HelpMessage = "Nimrod Compiler Version $1 (" & compileDate & ") [$2: $3]\n" &
"Copyright (c) 2004-2011 by Andreas Rumpf\n"
@@ -49,7 +49,7 @@ Options:
-f, --forceBuild force rebuilding of all modules
--stackTrace:on|off turn stack tracing on|off
--lineTrace:on|off turn line tracing on|off
--debugger:on|off turn Embedded Nimrod Debugger on|off
--threads:on|off turn support for multi-threading on|off
-x, --checks:on|off turn all runtime checks on|off
--objChecks:on|off turn obj conversion checks on|off
--fieldChecks:on|off turn case variant field checks on|off
@@ -92,6 +92,7 @@ Advanced options:
--os:SYMBOL set the target operating system (cross-compilation)
--cpu:SYMBOL set the target processor (cross-compilation)
--debuginfo enables debug information
--debugger:on|off turn Embedded Nimrod Debugger on|off
-t, --passc:OPTION pass an option to the C compiler
-l, --passl:OPTION pass an option to the linker
--genMapping generate a mapping file containing
@@ -212,9 +213,6 @@ proc ProcessSpecificNote(arg: string, state: TSpecialWord, pass: TCmdlinePass,
of wOn: incl(gNotes, n)
of wOff: excl(gNotes, n)
else: liMessage(info, errOnOrOffExpectedButXFound, arg)
proc processPath(path: string): string =
result = UnixToNativePath(path % ["nimrod", getPrefixDir(), "lib", libpath])
proc processCompile(filename: string) =
var found = findFile(filename)
@@ -269,20 +267,32 @@ proc testCompileOption*(switch: string, info: TLineInfo): bool =
of wRun, wR: result = contains(gGlobalOptions, optRun)
of wSymbolFiles: result = contains(gGlobalOptions, optSymbolFiles)
of wGenScript: result = contains(gGlobalOptions, optGenScript)
of wThreads: result = contains(gGlobalOptions, optThreads)
else: InvalidCmdLineOption(passCmd1, switch, info)
proc processPath(path: string): string =
result = UnixToNativePath(path % ["nimrod", getPrefixDir(), "lib", libpath,
"home", removeTrailingDirSep(os.getHomeDir())])
proc addPath(path: string) =
if not contains(options.searchPaths, path):
lists.PrependStr(options.searchPaths, path)
proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =
var
theOS: TSystemOS
cpu: TSystemCPU
key, val, path: string
key, val: string
case whichKeyword(switch)
of wPath, wP:
expectArg(switch, arg, pass, info)
path = processPath(arg)
if not contains(options.searchPaths, path):
lists.PrependStr(options.searchPaths, path)
addPath(processPath(arg))
#discard lists.IncludeStr(options.searchPaths, path)
of wRecursivePath:
expectArg(switch, arg, pass, info)
var path = processPath(arg)
for p in os.walkDirRec(path, {pcDir}): addPath(p)
addPath(path)
of wOut, wO:
expectArg(switch, arg, pass, info)
options.outFile = arg
@@ -356,6 +366,7 @@ proc processSwitch(switch, arg: string, pass: TCmdlinePass, info: TLineInfo) =
of wLineDir: ProcessOnOffSwitch({optLineDir}, arg, pass, info)
of wAssertions, wA: ProcessOnOffSwitch({optAssert}, arg, pass, info)
of wDeadCodeElim: ProcessOnOffSwitchG({optDeadCodeElim}, arg, pass, info)
of wThreads: ProcessOnOffSwitchG({optThreads}, arg, pass, info)
of wOpt:
expectArg(switch, arg, pass, info)
case whichKeyword(arg)

View File

@@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -21,28 +21,12 @@ type
m*: PSym
mode*: TOverloadIterMode
proc getSymRepr*(s: PSym): string
proc CloseScope*(tab: var TSymTab)
proc AddSym*(t: var TStrTable, n: PSym)
proc addDecl*(c: PContext, sym: PSym)
proc addDeclAt*(c: PContext, sym: PSym, at: Natural)
proc addOverloadableSymAt*(c: PContext, fn: PSym, at: Natural)
proc addInterfaceDecl*(c: PContext, sym: PSym)
proc addInterfaceOverloadableSymAt*(c: PContext, sym: PSym, at: int)
proc lookUp*(c: PContext, n: PNode): PSym
# Looks up a symbol. Generates an error in case of nil.
proc QualifiedLookUp*(c: PContext, n: PNode, ambiguousCheck: bool): PSym
proc InitOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym
proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym
# implementation
proc getSymRepr(s: PSym): string =
proc getSymRepr*(s: PSym): string =
case s.kind
of skProc, skMethod, skConverter, skIterator: result = getProcHeader(s)
else: result = s.name.s
proc CloseScope(tab: var TSymTab) =
proc CloseScope*(tab: var TSymTab) =
var
it: TTabIter
s: PSym
@@ -59,14 +43,14 @@ proc CloseScope(tab: var TSymTab) =
s = NextIter(it, tab.stack[tab.tos - 1])
astalgo.rawCloseScope(tab)
proc AddSym(t: var TStrTable, n: PSym) =
proc AddSym*(t: var TStrTable, n: PSym) =
if StrTableIncl(t, n): liMessage(n.info, errAttemptToRedefine, n.name.s)
proc addDecl(c: PContext, sym: PSym) =
proc addDecl*(c: PContext, sym: PSym) =
if SymTabAddUnique(c.tab, sym) == Failure:
liMessage(sym.info, errAttemptToRedefine, sym.Name.s)
proc addDeclAt(c: PContext, sym: PSym, at: Natural) =
proc addDeclAt*(c: PContext, sym: PSym, at: Natural) =
if SymTabAddUniqueAt(c.tab, sym, at) == Failure:
liMessage(sym.info, errAttemptToRedefine, sym.Name.s)
@@ -81,7 +65,7 @@ proc addInterfaceDeclAt*(c: PContext, sym: PSym, at: Natural) =
addDeclAt(c, sym, at)
AddInterfaceDeclAux(c, sym)
proc addOverloadableSymAt(c: PContext, fn: PSym, at: Natural) =
proc addOverloadableSymAt*(c: PContext, fn: PSym, at: Natural) =
if not (fn.kind in OverloadableSyms):
InternalError(fn.info, "addOverloadableSymAt")
var check = StrTableGet(c.tab.stack[at], fn.name)
@@ -89,17 +73,17 @@ proc addOverloadableSymAt(c: PContext, fn: PSym, at: Natural) =
liMessage(fn.info, errAttemptToRedefine, fn.Name.s)
SymTabAddAt(c.tab, fn, at)
proc addInterfaceDecl(c: PContext, sym: PSym) =
proc addInterfaceDecl*(c: PContext, sym: PSym) =
# it adds the symbol to the interface if appropriate
addDecl(c, sym)
AddInterfaceDeclAux(c, sym)
proc addInterfaceOverloadableSymAt(c: PContext, sym: PSym, at: int) =
proc addInterfaceOverloadableSymAt*(c: PContext, sym: PSym, at: int) =
# it adds the symbol to the interface if appropriate
addOverloadableSymAt(c, sym, at)
AddInterfaceDeclAux(c, sym)
proc lookUp(c: PContext, n: PNode): PSym =
proc lookUp*(c: PContext, n: PNode): PSym =
# Looks up a symbol. Generates an error in case of nil.
case n.kind
of nkAccQuoted:
@@ -118,26 +102,32 @@ proc lookUp(c: PContext, n: PNode): PSym =
liMessage(n.info, errUseQualifier, result.name.s)
if result.kind == skStub: loadStub(result)
proc QualifiedLookUp(c: PContext, n: PNode, ambiguousCheck: bool): PSym =
type
TLookupFlag* = enum
checkAmbiguity, checkUndeclared
proc QualifiedLookUp*(c: PContext, n: PNode, flags = {checkUndeclared}): PSym =
case n.kind
of nkIdent:
result = SymtabGet(c.Tab, n.ident)
if result == nil:
if result == nil and checkUndeclared in flags:
liMessage(n.info, errUndeclaredIdentifier, n.ident.s)
elif ambiguousCheck and IntSetContains(c.AmbiguousSymbols, result.id):
elif checkAmbiguity in flags and IntSetContains(c.AmbiguousSymbols,
result.id):
liMessage(n.info, errUseQualifier, n.ident.s)
of nkSym:
#
# result := SymtabGet(c.Tab, n.sym.name);
# if result = nil then
# result = SymtabGet(c.Tab, n.sym.name)
# if result == nil:
# liMessage(n.info, errUndeclaredIdentifier, n.sym.name.s)
# else
result = n.sym
if ambiguousCheck and IntSetContains(c.AmbiguousSymbols, result.id):
if checkAmbiguity in flags and IntSetContains(c.AmbiguousSymbols,
result.id):
liMessage(n.info, errUseQualifier, n.sym.name.s)
of nkDotExpr:
result = nil
var m = qualifiedLookUp(c, n.sons[0], false)
var m = qualifiedLookUp(c, n.sons[0], flags*{checkUndeclared})
if (m != nil) and (m.kind == skModule):
var ident: PIdent = nil
if (n.sons[1].kind == nkIdent):
@@ -150,17 +140,17 @@ proc QualifiedLookUp(c: PContext, n: PNode, ambiguousCheck: bool): PSym =
result = StrTableGet(c.tab.stack[ModuleTablePos], ident)
else:
result = StrTableGet(m.tab, ident)
if result == nil:
if result == nil and checkUndeclared in flags:
liMessage(n.sons[1].info, errUndeclaredIdentifier, ident.s)
else:
elif checkUndeclared in flags:
liMessage(n.sons[1].info, errIdentifierExpected, renderTree(n.sons[1]))
of nkAccQuoted:
result = QualifiedLookup(c, n.sons[0], ambiguousCheck)
result = QualifiedLookup(c, n.sons[0], flags)
else:
result = nil #liMessage(n.info, errIdentifierExpected, '')
result = nil
if (result != nil) and (result.kind == skStub): loadStub(result)
proc InitOverloadIter(o: var TOverloadIter, c: PContext, n: PNode): PSym =
proc InitOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
var ident: PIdent
result = nil
case n.kind
@@ -173,17 +163,16 @@ proc InitOverloadIter(o: var TOverloadIter, c: PContext, n: PNode): PSym =
result = InitIdentIter(o.it, c.tab.stack[o.stackPtr], n.ident)
of nkSym:
result = n.sym
o.mode = oimDone #
# o.stackPtr := c.tab.tos;
# o.mode := oimNoQualifier;
# while (result = nil) do begin
# dec(o.stackPtr);
# if o.stackPtr < 0 then break;
# result := InitIdentIter(o.it, c.tab.stack[o.stackPtr], n.sym.name);
# end;
o.mode = oimDone
# o.stackPtr = c.tab.tos
# o.mode = oimNoQualifier
# while result == nil:
# dec(o.stackPtr)
# if o.stackPtr < 0: break
# result = InitIdentIter(o.it, c.tab.stack[o.stackPtr], n.sym.name)
of nkDotExpr:
o.mode = oimOtherModule
o.m = qualifiedLookUp(c, n.sons[0], false)
o.m = qualifiedLookUp(c, n.sons[0])
if (o.m != nil) and (o.m.kind == skModule):
ident = nil
if (n.sons[1].kind == nkIdent):
@@ -210,7 +199,7 @@ proc InitOverloadIter(o: var TOverloadIter, c: PContext, n: PNode): PSym =
nil
if (result != nil) and (result.kind == skStub): loadStub(result)
proc nextOverloadIter(o: var TOverloadIter, c: PContext, n: PNode): PSym =
proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym =
case o.mode
of oimDone:
result = nil
@@ -222,8 +211,8 @@ proc nextOverloadIter(o: var TOverloadIter, c: PContext, n: PNode): PSym =
while (result == nil):
dec(o.stackPtr)
if o.stackPtr < 0: break
result = InitIdentIter(o.it, c.tab.stack[o.stackPtr], o.it.name) # BUGFIX:
# o.it.name <-> n.ident
result = InitIdentIter(o.it, c.tab.stack[o.stackPtr], o.it.name)
# BUGFIX: o.it.name <-> n.ident
else:
result = nil
of oimSelfModule:

View File

@@ -30,7 +30,8 @@ type # please make sure we have under 32 options
TOptions* = set[TOption]
TGlobalOption* = enum
gloptNone, optForceFullMake, optBoehmGC, optRefcGC, optDeadCodeElim,
optListCmd, optCompileOnly, optNoLinking, optSafeCode, # only allow safe code
optListCmd, optCompileOnly, optNoLinking,
optSafeCode, # only allow safe code
optCDebug, # turn on debugging information
optGenDynLib, # generate a dynamic library
optGenGuiApp, # generate a GUI application
@@ -40,7 +41,8 @@ type # please make sure we have under 32 options
optSymbolFiles, # use symbol files for speeding up compilation
optSkipConfigFile, # skip the general config file
optSkipProjConfigFile, # skip the project's config file
optNoMain # do not generate a "main" proc
optNoMain, # do not generate a "main" proc
optThreads # support for multi-threading
TGlobalOptions* = set[TGlobalOption]
TCommands* = enum # Nimrod's commands
cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC,
@@ -142,7 +144,7 @@ proc shortenDir(dir: string): string =
return copy(dir, len(prefix))
result = dir
proc removeTrailingDirSep(path: string): string =
proc removeTrailingDirSep*(path: string): string =
if (len(path) > 0) and (path[len(path) - 1] == dirSep):
result = copy(path, 0, len(path) - 2)
else:

View File

@@ -631,7 +631,7 @@ proc makeDeref(n: PNode): PNode =
proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
## returns nil if it's not a built-in field access
var s = qualifiedLookup(c, n, true) # check for ambiguity
var s = qualifiedLookup(c, n, {checkAmbiguity, checkUndeclared})
if s != nil:
return semSym(c, n, s, flags)
@@ -940,7 +940,7 @@ proc semMacroStmt(c: PContext, n: PNode, semCheck: bool = true): PNode =
var a: PNode
if isCallExpr(n.sons[0]): a = n.sons[0].sons[0]
else: a = n.sons[0]
var s = qualifiedLookup(c, a, false)
var s = qualifiedLookup(c, a, {checkUndeclared})
if s != nil:
case s.kind
of skMacro:
@@ -1014,7 +1014,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
of nkCall, nkInfix, nkPrefix, nkPostfix, nkCommand, nkCallStrLit:
# check if it is an expression macro:
checkMinSonsLen(n, 1)
var s = qualifiedLookup(c, n.sons[0], false)
var s = qualifiedLookup(c, n.sons[0], {checkUndeclared})
if s != nil:
case s.kind
of skMacro: result = semMacroExpr(c, n, s)
@@ -1037,10 +1037,10 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode =
result = semMacroStmt(c, n)
of nkBracketExpr:
checkMinSonsLen(n, 1)
var s = qualifiedLookup(c, n.sons[0], false)
var s = qualifiedLookup(c, n.sons[0], {checkUndeclared})
if s != nil and s.kind in {skProc, skMethod, skConverter, skIterator}:
# type parameters: partial generic specialization
result = partialSpecialization(c, n, s)
result = explictGenericInstantiation(c, n, s)
else:
result = semArrayAccess(c, n, flags)
of nkPragmaExpr:

View File

@@ -21,12 +21,14 @@ type
TSemGenericFlags = set[TSemGenericFlag]
proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags = {}): PNode
proc semGenericStmtScope(c: PContext, n: PNode, flags: TSemGenericFlags = {}): PNode =
proc semGenericStmtScope(c: PContext, n: PNode,
flags: TSemGenericFlags = {}): PNode =
openScope(c.tab)
result = semGenericStmt(c, n, flags)
closeScope(c.tab)
proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym): PNode =
incl(s.flags, sfUsed)
case s.kind
of skUnknown:
# Introduced in this pass! Leave it as an identifier.
@@ -55,20 +57,29 @@ proc getIdentNode(n: PNode): PNode =
illFormedAst(n)
result = nil
# of nkAccQuoted:
# s = lookUp(c, n)
# if withinBind in flags: result = symChoice(c, n, s)
# else: result = semGenericStmtSymbol(c, n, s)
proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags = {}): PNode =
var
L: int
a: PNode
s: PSym
result = n
if n == nil: return
case n.kind
of nkIdent, nkAccQuoted:
s = lookUp(c, n)
if withinBind in flags: result = symChoice(c, n, s)
else: result = semGenericStmtSymbol(c, n, s)
of nkIdent:
var s = SymtabGet(c.Tab, n.ident)
if s == nil:
# no error if symbol cannot be bound, unless in ``bind`` context:
if withinBind in flags:
liMessage(n.info, errUndeclaredIdentifier, n.ident.s)
else:
if withinBind in flags: result = symChoice(c, n, s)
else: result = semGenericStmtSymbol(c, n, s)
of nkDotExpr:
s = QualifiedLookUp(c, n, true)
var s = QualifiedLookUp(c, n, {})
if s != nil: result = semGenericStmtSymbol(c, n, s)
of nkSym..nkNilLit:
nil
@@ -77,8 +88,9 @@ proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags = {}): PNode
of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkCommand, nkCallStrLit:
# check if it is an expression macro:
checkMinSonsLen(n, 1)
s = qualifiedLookup(c, n.sons[0], false)
if (s != nil):
var s = qualifiedLookup(c, n.sons[0], {})
if s != nil:
incl(s.flags, sfUsed)
case s.kind
of skMacro:
return semMacroExpr(c, n, s, false)
@@ -100,16 +112,16 @@ proc semGenericStmt(c: PContext, n: PNode, flags: TSemGenericFlags = {}): PNode
of nkMacroStmt:
result = semMacroStmt(c, n, false)
of nkIfStmt:
for i in countup(0, sonsLen(n) - 1):
for i in countup(0, sonsLen(n)-1):
n.sons[i] = semGenericStmtScope(c, n.sons[i])
of nkWhileStmt:
openScope(c.tab)
for i in countup(0, sonsLen(n) - 1): n.sons[i] = semGenericStmt(c, n.sons[i])
for i in countup(0, sonsLen(n)-1): n.sons[i] = semGenericStmt(c, n.sons[i])
closeScope(c.tab)
of nkCaseStmt:
openScope(c.tab)
n.sons[0] = semGenericStmt(c, n.sons[0])
for i in countup(1, sonsLen(n) - 1):
for i in countup(1, sonsLen(n)-1):
a = n.sons[i]
checkMinSonsLen(a, 1)
L = sonsLen(a)

View File

@@ -106,6 +106,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
else:
result.typ = newTypeS(tyProc, c)
addSon(result.typ, nil)
result.typ.callConv = fn.typ.callConv
oldPrc = GenericCacheGet(c, fn, result)
if oldPrc == nil:
# add it here, so that recursive generic procs are possible:
@@ -244,10 +245,3 @@ proc generateTypeInstance(p: PContext, pt: TIdTable, arg: PNode,
result = ReplaceTypeVarsT(cl, t)
popInfoContext()
proc partialSpecialization(c: PContext, n: PNode, s: PSym): PNode =
for i in 1..sonsLen(n)-1:
n.sons[i].typ = semTypeNode(c, n.sons[i], nil)
# we cannot check for the proper number of type parameters because in
# `f[a,b](x, y)` `f` is not resolved yet properly.
# XXX: BUG this should be checked somehow!
result = n

View File

@@ -162,7 +162,7 @@ proc propertyWriteAccess(c: PContext, n, a: PNode): PNode =
addSon(result, newIdentNode(getIdent(id.s & '='), n.info))
# a[0] is already checked for semantics, that does ``builtinFieldAccess``
# this is ugly. XXX Semantic checking should use the ``nfSem`` flag for
# nodes!
# nodes?
addSon(result, a[0])
addSon(result, semExpr(c, n[1]))
result = semDirectCallAnalyseEffects(c, result, {})
@@ -182,18 +182,6 @@ proc semAsgn(c: PContext, n: PNode): PNode =
a = builtinFieldAccess(c, a, {efLValue})
if a == nil:
return propertyWriteAccess(c, n, n[0])
when false:
checkSonsLen(a, 2)
var id = considerAcc(a.sons[1])
result = newNodeI(nkCall, n.info)
addSon(result, newIdentNode(getIdent(id.s & '='), n.info))
addSon(result, semExpr(c, a.sons[0]))
addSon(result, semExpr(c, n.sons[1]))
result = semDirectCallAnalyseEffects(c, result, {})
if result != nil:
fixAbstractType(c, result)
analyseIfAddressTakenInCall(c, result)
return
of nkBracketExpr:
# a[i..j] = x
# --> `[..]=`(a, i, j, x)
@@ -204,7 +192,6 @@ proc semAsgn(c: PContext, n: PNode): PNode =
return semExprNoType(c, result)
else:
a = semExprWithType(c, a, {efLValue})
#n.sons[0] = semExprWithType(c, n.sons[0], {efLValue})
n.sons[0] = a
n.sons[1] = semExprWithType(c, n.sons[1])
var le = a.typ
@@ -288,7 +275,6 @@ proc semVar(c: PContext, n: PNode): PNode =
else:
def = nil
if not typeAllowed(typ, skVar):
#debug(typ);
liMessage(a.info, errXisNoType, typeToString(typ))
tup = skipTypes(typ, {tyGenericInst})
if a.kind == nkVarTuple:

View File

@@ -178,7 +178,7 @@ proc semOrdinal(c: PContext, n: PNode, prev: PType): PType =
liMessage(n.info, errXExpectsOneTypeParam, "ordinal")
proc semTypeIdent(c: PContext, n: PNode): PSym =
result = qualifiedLookup(c, n, true)
result = qualifiedLookup(c, n, {checkAmbiguity, checkUndeclared})
if (result != nil):
markUsed(n, result)
if result.kind != skType: liMessage(n.info, errTypeExpected)

View File

@@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2010 Andreas Rumpf
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -82,11 +82,11 @@ proc cmpCandidates(a, b: TCandidate): int =
result = a.convMatches - b.convMatches
proc writeMatches(c: TCandidate) =
Writeln(stdout, "exact matches: " & $(c.exactMatches))
Writeln(stdout, "subtype matches: " & $(c.subtypeMatches))
Writeln(stdout, "conv matches: " & $(c.convMatches))
Writeln(stdout, "intconv matches: " & $(c.intConvMatches))
Writeln(stdout, "generic matches: " & $(c.genericMatches))
Writeln(stdout, "exact matches: " & $c.exactMatches)
Writeln(stdout, "subtype matches: " & $c.subtypeMatches)
Writeln(stdout, "conv matches: " & $c.convMatches)
Writeln(stdout, "intconv matches: " & $c.intConvMatches)
Writeln(stdout, "generic matches: " & $c.genericMatches)
proc getNotFoundError(c: PContext, n: PNode): string =
# Gives a detailed error message; this is separated from semDirectCall,
@@ -710,8 +710,7 @@ proc semDirectCallWithBinding(c: PContext, n, f: PNode, filter: TSymKinds,
elif y.state == csMatch and cmpCandidates(x, y) == 0 and
not sameMethodDispatcher(x.calleeSym, y.calleeSym):
if x.state != csMatch:
InternalError(n.info, "x.state is not csMatch") #writeMatches(x);
#writeMatches(y);
InternalError(n.info, "x.state is not csMatch")
liMessage(n.Info, errGenerated, msgKindToString(errAmbiguousCallXYZ) % [
getProcHeader(x.calleeSym), getProcHeader(y.calleeSym),
x.calleeSym.Name.s])
@@ -740,3 +739,20 @@ proc semDirectCall(c: PContext, n: PNode, filter: TSymKinds): PNode =
initialBinding = nil
result = semDirectCallWithBinding(c, n, f, filter, initialBinding)
proc explictGenericInstantiation(c: PContext, n: PNode, s: PSym): PNode =
assert n.kind == nkBracketExpr
for i in 1..sonsLen(n)-1:
n.sons[i].typ = semTypeNode(c, n.sons[i], nil)
# we cannot check for the proper number of type parameters because in
# `f[a,b](x, y)` `f` is not resolved yet properly.
# XXX: BUG this should be checked somehow!
assert n.sons[0].kind == nkSym
var x: TCandidate
initCandidate(x, s, n)
var newInst = generateInstance(c, s, x.bindings, n.info)
markUsed(n, s)
result = newSymNode(newInst)
result.info = n.info

View File

@@ -1,7 +1,7 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2010 Andreas Rumpf
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
@@ -747,7 +747,7 @@ proc typeAllowedNode(marker: var TIntSet, n: PNode, kind: TSymKind): bool =
result = true
if n != nil:
result = typeAllowedAux(marker, n.typ, kind)
if not result: debug(n.typ)
#if not result: debug(n.typ)
if result:
case n.kind
of nkNone..nkNilLit:

View File

@@ -59,7 +59,8 @@ type
wPretty,
wDoc, wGenDepend, wListDef, wCheck, wParse, wScan, wJs, wOC,
wRst2html, wRst2tex, wI,
wWrite, wPutEnv, wPrependEnv, wAppendEnv, wThreadVar, wEmit
wWrite, wPutEnv, wPrependEnv, wAppendEnv, wThreadVar, wEmit, wThreads,
wRecursivePath
TSpecialWords* = set[TSpecialWord]
@@ -107,7 +108,8 @@ const
"compiletooc",
"pretty", "doc", "gendepend", "listdef", "check", "parse", "scan",
"js", "oc", "rst2html", "rst2tex", "i",
"write", "putenv", "prependenv", "appendenv", "threadvar", "emit"]
"write", "putenv", "prependenv", "appendenv", "threadvar", "emit",
"threads", "recursivepath"]
proc whichKeyword*(id: PIdent): TSpecialWord
proc whichKeyword*(id: String): TSpecialWord

View File

@@ -1,6 +1,6 @@
- built-in serialization
- thread support: threadvar on Windows seems broken;
add --deadlock_prevention:on|off switch
- built-in serialization
- ban ``nil`` from the AST. This might also fix bugs concerning macros.
@@ -66,7 +66,8 @@ Low priority
- tlastmod returns wrong results on BSD (Linux, MacOS X: works)
- nested tuple unpacking
- fast assignment optimization for TPeg
- better error messages for used keywords as identifiers
- case statement branches should support constant sets
Library
-------

View File

@@ -47,13 +47,13 @@ if [ $# -eq 1 ] ; then
docdir="$1/?proj/doc"
datadir="$1/?proj/data"
mkdir -p $1/?proj
mkdir -p $bindir
mkdir -p $configdir
mkdir -p $1/?proj || exit 1
mkdir -p $bindir || exit 1
mkdir -p $configdir || exit 1
;;
esac
mkdir -p $libdir
mkdir -p $docdir
mkdir -p $libdir || exit 1
mkdir -p $docdir || exit 1
echo "copying files..."
#var createdDirs = newStringTable()
#for cat in fcConfig..fcLib:
@@ -63,30 +63,30 @@ if [ $# -eq 1 ] ; then
# mk = unixDirVars[cat] & "/" & mk
# if not createdDirs.hasKey(mk):
# createdDirs[mk] = "true"
mkdir -p ?mk
mkdir -p ?mk || exit 1
# end if
# end if
# end for
#end for
#for f in items(c.cat[fcUnixBin]):
cp ?f $bindir/?f.skipRoot
cp ?f $bindir/?f.skipRoot || exit 1
chmod 755 $bindir/?f.skipRoot
#end for
#for f in items(c.cat[fcConfig]):
cp ?f $configdir/?f.skipRoot
cp ?f $configdir/?f.skipRoot || exit 1
chmod 644 $configdir/?f.skipRoot
#end for
#for f in items(c.cat[fcData]):
cp ?f $datadir/?f.skipRoot
cp ?f $datadir/?f.skipRoot || exit 1
chmod 644 $datadir/?f.skipRoot
#end for
#for f in items(c.cat[fcDoc]):
cp ?f $docdir/?f.skipRoot
cp ?f $docdir/?f.skipRoot || exit 1
chmod 644 $docdir/?f.skipRoot
#end for
#for f in items(c.cat[fcLib]):
cp ?f $libdir/?f.skipRoot
cp ?f $libdir/?f.skipRoot || exit 1
chmod 644 $libdir/?f.skipRoot
#end for

View File

@@ -33,6 +33,8 @@ Changes affecting backwards compatibility
Additions
---------
- Added ``scgi`` module.
- Added ``smtp`` module.
- Added ``re.findAll``, ``pegs.findAll``.
- Added ``os.findExe``.
- Added ``strutils.align``, ``strutils.tokenize``, ``strutils.wordWrap``.
@@ -40,7 +42,7 @@ Additions
- Pegs support new built-ins: ``\letter``, ``\upper``, ``\lower``,
``\title``, ``\white``.
- Pegs support the new built-in ``\skip`` operation.
- Source code filters are now properly documented.
- Source code filters are now documented.
- Added ``emit`` pragma for direct code generator control.
- Additional operations were added to the ``complex`` module.
- Added ``strutils.formatFloat``, ``strutils.formatBiggestFloat``.