much more efficient rod file generation

This commit is contained in:
Araq 2011-10-18 17:21:51 +02:00
commit 4de84024e5
18 changed files with 611 additions and 557 deletions

View file

@ -10,7 +10,8 @@
# abstract syntax tree + symbol table # abstract syntax tree + symbol table
import import
msgs, hashes, nversion, options, strutils, crc, ropes, idents, lists, intsets msgs, hashes, nversion, options, strutils, crc, ropes, idents, lists,
intsets, idgen
const const
ImportTablePos* = 0 ImportTablePos* = 0
@ -377,7 +378,6 @@ type
type type
PNode* = ref TNode PNode* = ref TNode
PNodePtr* = ptr PNode
TNodeSeq* = seq[PNode] TNodeSeq* = seq[PNode]
PType* = ref TType PType* = ref TType
PSym* = ref TSym PSym* = ref TSym
@ -577,12 +577,6 @@ const
resultPos* = 5 resultPos* = 5
dispatcherPos* = 6 dispatcherPos* = 6
var gId*: int
proc getID*(): int {.inline.}
proc setID*(id: int) {.inline.}
proc IDsynchronizationPoint*(idRange: int)
# creator procs: # creator procs:
proc NewSym*(symKind: TSymKind, Name: PIdent, owner: PSym): PSym proc NewSym*(symKind: TSymKind, Name: PIdent, owner: PSym): PSym
proc NewType*(kind: TTypeKind, owner: PSym): PType proc NewType*(kind: TTypeKind, owner: PSym): PType
@ -660,27 +654,6 @@ proc leValue*(a, b: PNode): bool
# a <= b? a, b are literals # a <= b? a, b are literals
proc ValueToString*(a: PNode): string proc ValueToString*(a: PNode): string
const
debugIds* = false
when debugIds:
var usedIds: TIntSet
proc registerID*(id: PIdObj) =
when debugIDs:
if (id.id == - 1) or ContainsOrIncl(usedIds, id.id):
InternalError("ID already used: " & $(id.id))
proc getID(): int =
result = gId
inc(gId)
proc setId(id: int) =
gId = max(gId, id + 1)
proc IDsynchronizationPoint(idRange: int) =
gId = (gId div IdRange + 1) * IdRange + 1
proc leValue(a, b: PNode): bool = proc leValue(a, b: PNode): bool =
# a <= b? # a <= b?
result = false result = false
@ -1032,4 +1005,3 @@ proc getStrOrChar*(a: PNode): string =
internalError(a.info, "getStrOrChar") internalError(a.info, "getStrOrChar")
result = "" result = ""
when debugIDs: usedIds = InitIntSet()

View file

@ -38,7 +38,7 @@ proc genHexLiteral(v: PNode): PRope =
proc getStrLit(m: BModule, s: string): PRope = proc getStrLit(m: BModule, s: string): PRope =
discard cgsym(m, "TGenericSeq") discard cgsym(m, "TGenericSeq")
result = con("TMP", toRope(getID())) result = con("TMP", toRope(backendId()))
appf(m.s[cfsData], "STRING_LITERAL($1, $2, $3);$n", appf(m.s[cfsData], "STRING_LITERAL($1, $2, $3);$n",
[result, makeCString(s), ToRope(len(s))]) [result, makeCString(s), ToRope(len(s))])
@ -70,8 +70,8 @@ proc genLiteral(p: BProc, v: PNode, ty: PType): PRope =
result = toRope("NIM_NIL") result = toRope("NIM_NIL")
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
if skipTypes(ty, abstractVarRange).kind == tyString: if skipTypes(ty, abstractVarRange).kind == tyString:
var id = NodeTableTestOrSet(p.module.dataCache, v, gid) var id = NodeTableTestOrSet(p.module.dataCache, v, gBackendId)
if id == gid: if id == gBackendId:
# string literal not found in the cache: # string literal not found in the cache:
result = ropecg(p.module, "((#NimStringDesc*) &$1)", result = ropecg(p.module, "((#NimStringDesc*) &$1)",
[getStrLit(p.module, v.strVal)]) [getStrLit(p.module, v.strVal)])
@ -123,11 +123,11 @@ proc genSetNode(p: BProc, n: PNode): PRope =
var size = int(getSize(n.typ)) var size = int(getSize(n.typ))
toBitSet(n, cs) toBitSet(n, cs)
if size > 8: if size > 8:
var id = NodeTableTestOrSet(p.module.dataCache, n, gid) var id = NodeTableTestOrSet(p.module.dataCache, n, gBackendId)
result = con("TMP", toRope(id)) result = con("TMP", toRope(id))
if id == gid: if id == gBackendId:
# not found in cache: # not found in cache:
inc(gid) inc(gBackendId)
appf(p.module.s[cfsData], "static NIM_CONST $1 $2 = $3;$n", appf(p.module.s[cfsData], "static NIM_CONST $1 $2 = $3;$n",
[getTypeDesc(p.module, n.typ), result, genRawSetData(cs, size)]) [getTypeDesc(p.module, n.typ), result, genRawSetData(cs, size)])
else: else:
@ -618,8 +618,8 @@ proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) =
v.r = ropef("$1.$2", [r, it.sons[2].sym.loc.r]) v.r = ropef("$1.$2", [r, it.sons[2].sym.loc.r])
genInExprAux(p, it, u, v, test) genInExprAux(p, it, u, v, test)
id = NodeTableTestOrSet(p.module.dataCache, id = NodeTableTestOrSet(p.module.dataCache,
newStrNode(nkStrLit, field.name.s), gid) newStrNode(nkStrLit, field.name.s), gBackendId)
if id == gid: strLit = getStrLit(p.module, field.name.s) if id == gBackendId: strLit = getStrLit(p.module, field.name.s)
else: strLit = con("TMP", toRope(id)) else: strLit = con("TMP", toRope(id))
if op.magic == mNot: if op.magic == mNot:
appcg(p, cpsStmts, appcg(p, cpsStmts,
@ -1581,11 +1581,11 @@ proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool =
if (nfAllConst in n.flags) and (d.k == locNone) and (sonsLen(n) > 0): if (nfAllConst in n.flags) and (d.k == locNone) and (sonsLen(n) > 0):
var t = getUniqueType(n.typ) var t = getUniqueType(n.typ)
discard getTypeDesc(p.module, t) # so that any fields are initialized discard getTypeDesc(p.module, t) # so that any fields are initialized
var id = NodeTableTestOrSet(p.module.dataCache, n, gid) var id = NodeTableTestOrSet(p.module.dataCache, n, gBackendId)
fillLoc(d, locData, t, con("TMP", toRope(id)), OnHeap) fillLoc(d, locData, t, con("TMP", toRope(id)), OnHeap)
if id == gid: if id == gBackendId:
# expression not found in the cache: # expression not found in the cache:
inc(gid) inc(gBackendId)
appf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n", appf(p.module.s[cfsData], "NIM_CONST $1 $2 = $3;$n",
[getTypeDesc(p.module, t), d.r, genConstExpr(p, n)]) [getTypeDesc(p.module, t), d.r, genConstExpr(p, n)])
result = true result = true

View file

@ -145,12 +145,10 @@ proc CacheGetType(tab: TIdTable, key: PType): PRope =
result = PRope(IdTableGet(tab, key)) result = PRope(IdTableGet(tab, key))
proc getTempName(): PRope = proc getTempName(): PRope =
result = ropeff("TMP$1", "%TMP$1", [toRope(gId)]) result = ropeff("TMP$1", "%TMP$1", [toRope(backendId())])
inc(gId)
proc getGlobalTempName(): PRope = proc getGlobalTempName(): PRope =
result = ropeff("TMP$1", "@TMP$1", [toRope(gId)]) result = ropeff("TMP$1", "@TMP$1", [toRope(backendId())])
inc(gId)
proc ccgIntroducedPtr(s: PSym): bool = proc ccgIntroducedPtr(s: PSym): bool =
var pt = skipTypes(s.typ, abstractInst) var pt = skipTypes(s.typ, abstractInst)

View file

@ -15,7 +15,7 @@ import
options, intsets, options, intsets,
nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os, nversion, nimsets, msgs, crc, bitsets, idents, lists, types, ccgutils, os,
times, ropes, math, passes, rodread, wordrecg, treetab, cgmeth, times, ropes, math, passes, rodread, wordrecg, treetab, cgmeth,
rodutils, renderer rodutils, renderer, idgen
when options.hasTinyCBackend: when options.hasTinyCBackend:
import tccgen import tccgen

View file

@ -1,14 +1,14 @@
# #
# #
# The Nimrod Compiler # The Nimrod Compiler
# (c) Copyright 2009 Andreas Rumpf # (c) Copyright 2011 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
# Identifier handling # Identifier handling
# An identifier is a shared non-modifiable string that can be compared by its # An identifier is a shared immutable string that can be compared by its
# id. This module is essential for the compiler's performance. # id. This module is essential for the compiler's performance.
import import
@ -25,35 +25,20 @@ type
next*: PIdent # for hash-table chaining next*: PIdent # for hash-table chaining
h*: THash # hash value of s h*: THash # hash value of s
proc getIdent*(identifier: string): PIdent
proc getIdent*(identifier: string, h: THash): PIdent
proc getIdent*(identifier: cstring, length: int, h: THash): PIdent
# special version for the scanner; the scanner's buffering scheme makes
# this horribly efficient. Most of the time no character copying is needed!
proc IdentEq*(id: PIdent, name: string): bool
# implementation
proc IdentEq(id: PIdent, name: string): bool =
result = id.id == getIdent(name).id
var buckets: array[0..4096 * 2 - 1, PIdent] var buckets: array[0..4096 * 2 - 1, PIdent]
proc cmpIgnoreStyle(a, b: cstring, blen: int): int = proc cmpIgnoreStyle(a, b: cstring, blen: int): int =
var var i = 0
aa, bb: char var j = 0
i, j: int
i = 0
j = 0
result = 1 result = 1
while j < blen: while j < blen:
while a[i] == '_': inc(i) while a[i] == '_': inc(i)
while b[j] == '_': inc(j) while b[j] == '_': inc(j)
# tolower inlined: # tolower inlined:
aa = a[i] var aa = a[i]
bb = b[j] var bb = b[j]
if (aa >= 'A') and (aa <= 'Z'): aa = chr(ord(aa) + (ord('a') - ord('A'))) if aa >= 'A' and aa <= 'Z': aa = chr(ord(aa) + (ord('a') - ord('A')))
if (bb >= 'A') and (bb <= 'Z'): bb = chr(ord(bb) + (ord('a') - ord('A'))) if bb >= 'A' and bb <= 'Z': bb = chr(ord(bb) + (ord('a') - ord('A')))
result = ord(aa) - ord(bb) result = ord(aa) - ord(bb)
if (result != 0) or (aa == '\0'): break if (result != 0) or (aa == '\0'): break
inc(i) inc(i)
@ -62,15 +47,12 @@ proc cmpIgnoreStyle(a, b: cstring, blen: int): int =
if a[i] != '\0': result = 1 if a[i] != '\0': result = 1
proc cmpExact(a, b: cstring, blen: int): int = proc cmpExact(a, b: cstring, blen: int): int =
var var i = 0
aa, bb: char var j = 0
i, j: int
i = 0
j = 0
result = 1 result = 1
while j < blen: while j < blen:
aa = a[i] var aa = a[i]
bb = b[j] var bb = b[j]
result = ord(aa) - ord(bb) result = ord(aa) - ord(bb)
if (result != 0) or (aa == '\0'): break if (result != 0) or (aa == '\0'): break
inc(i) inc(i)
@ -78,23 +60,13 @@ proc cmpExact(a, b: cstring, blen: int): int =
if result == 0: if result == 0:
if a[i] != '\0': result = 1 if a[i] != '\0': result = 1
proc getIdent(identifier: string): PIdent = var wordCounter = 1
result = getIdent(cstring(identifier), len(identifier),
hashIgnoreStyle(identifier))
proc getIdent(identifier: string, h: THash): PIdent = proc getIdent*(identifier: cstring, length: int, h: THash): PIdent =
result = getIdent(cstring(identifier), len(identifier), h) var idx = h and high(buckets)
var wordCounter: int = 1
proc getIdent(identifier: cstring, length: int, h: THash): PIdent =
var
idx, id: int
last: PIdent
idx = h and high(buckets)
result = buckets[idx] result = buckets[idx]
last = nil var last: PIdent = nil
id = 0 var id = 0
while result != nil: while result != nil:
if cmpExact(cstring(result.s), identifier, length) == 0: if cmpExact(cstring(result.s), identifier, length) == 0:
if last != nil: if last != nil:
@ -104,16 +76,6 @@ proc getIdent(identifier: cstring, length: int, h: THash): PIdent =
buckets[idx] = result buckets[idx] = result
return return
elif cmpIgnoreStyle(cstring(result.s), identifier, length) == 0: elif cmpIgnoreStyle(cstring(result.s), identifier, length) == 0:
#if (id <> 0) and (id <> result.id) then begin
# result := buckets[idx];
# writeln('current id ', id);
# for i := 0 to len-1 do write(identifier[i]);
# writeln;
# while result <> nil do begin
# writeln(result.s, ' ', result.id);
# result := result.next
# end
# end;
assert((id == 0) or (id == result.id)) assert((id == 0) or (id == result.id))
id = result.id id = result.id
last = result last = result
@ -121,12 +83,22 @@ proc getIdent(identifier: cstring, length: int, h: THash): PIdent =
new(result) new(result)
result.h = h result.h = h
result.s = newString(length) result.s = newString(length)
for i in countup(0, length + 0 - 1): result.s[i] = identifier[i - 0] for i in countup(0, length - 1): result.s[i] = identifier[i]
result.next = buckets[idx] result.next = buckets[idx]
buckets[idx] = result buckets[idx] = result
if id == 0: if id == 0:
inc(wordCounter) inc(wordCounter)
result.id = - wordCounter result.id = -wordCounter
else: else:
result.id = id # writeln('new word ', result.s); result.id = id
proc getIdent*(identifier: string): PIdent =
result = getIdent(cstring(identifier), len(identifier),
hashIgnoreStyle(identifier))
proc getIdent*(identifier: string, h: THash): PIdent =
result = getIdent(cstring(identifier), len(identifier), h)
proc IdentEq*(id: PIdent, name: string): bool =
result = id.id == getIdent(name).id

57
compiler/idgen.nim Normal file
View file

@ -0,0 +1,57 @@
#
#
# The Nimrod Compiler
# (c) Copyright 2011 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## This module contains a simple persistent id generator.
import idents, strutils, os
var gFrontEndId, gBackendId*: int
const
debugIds* = false
when debugIds:
import intsets
var usedIds = InitIntSet()
proc registerID*(id: PIdObj) =
when debugIDs:
if (id.id == - 1) or ContainsOrIncl(usedIds, id.id):
InternalError("ID already used: " & $id.id)
proc getID*(): int {.inline.} =
result = gFrontEndId
inc(gFrontEndId)
proc backendId*(): int {.inline.} =
result = gBackendId
inc(gBackendId)
proc setId*(id: int) {.inline.} =
gFrontEndId = max(gFrontEndId, id + 1)
proc IDsynchronizationPoint*(idRange: int) =
gFrontEndId = (gFrontEndId div IdRange + 1) * IdRange + 1
proc saveMaxIds*(project: string) =
var f = open(project.addFileExt("gid"), fmWrite)
f.writeln($gFrontEndId)
f.writeln($gBackEndId)
f.close()
proc loadMaxIds*(project: string) =
var f: TFile
if open(f, project.addFileExt("gid"), fmRead):
var frontEndId = parseInt(f.readLine)
var backEndId = parseInt(f.readLine)
gFrontEndId = max(gFrontEndId, frontEndId)
gBackEndId = max(gBackEndId, backEndId)
f.close()

View file

@ -15,7 +15,7 @@ import
os, lists, condsyms, rodread, rodwrite, ropes, trees, os, lists, condsyms, rodread, rodwrite, ropes, trees,
wordrecg, sem, semdata, idents, passes, docgen, extccomp, wordrecg, sem, semdata, idents, passes, docgen, extccomp,
cgen, ecmasgen, cgen, ecmasgen,
platform, nimconf, importer, passaux, depends, transf, evals, types platform, nimconf, importer, passaux, depends, transf, evals, types, idgen
const const
has_LLVM_Backend = false has_LLVM_Backend = false
@ -24,24 +24,17 @@ when has_LLVM_Backend:
import llvmgen import llvmgen
proc MainCommand*(cmd, filename: string) proc MainCommand*(cmd, filename: string)
# implementation
# ------------------ module handling ----------------------------------------- # ------------------ module handling -----------------------------------------
type type
TFileModuleRec{.final.} = object TFileModuleRec = tuple[filename: string, module: PSym]
filename*: string
module*: PSym
TFileModuleMap = seq[TFileModuleRec] TFileModuleMap = seq[TFileModuleRec]
var compMods: TFileModuleMap = @[] var compMods: TFileModuleMap = @[] # all compiled modules
proc registerModule(filename: string, module: PSym) = proc registerModule(filename: string, module: PSym) =
# all compiled modules compMods.add((filename, module))
var length = len(compMods)
setlen(compMods, length + 1)
compMods[length].filename = filename
compMods[length].module = module
proc getModule(filename: string): PSym = proc getModule(filename: string): PSym =
for i in countup(0, high(compMods)): for i in countup(0, high(compMods)):
@ -65,23 +58,22 @@ proc newModule(filename: string): PSym =
RegisterModule(filename, result) RegisterModule(filename, result)
StrTableAdd(result.tab, result) # a module knows itself StrTableAdd(result.tab, result) # a module knows itself
proc CompileModule(filename: string, isMainFile, isSystemFile: bool): PSym proc CompileModule(filename: string, flags: TSymFlags): PSym
proc importModule(filename: string): PSym = proc importModule(filename: string): PSym =
# this is called by the semantic checking phase # this is called by the semantic checking phase
result = getModule(filename) result = getModule(filename)
if result == nil: if result == nil:
# compile the module # compile the module
result = compileModule(filename, false, false) result = compileModule(filename, {})
elif sfSystemModule in result.flags: elif sfSystemModule in result.flags:
LocalError(result.info, errAttemptToRedefine, result.Name.s) LocalError(result.info, errAttemptToRedefine, result.Name.s)
proc CompileModule(filename: string, isMainFile, isSystemFile: bool): PSym = proc CompileModule(filename: string, flags: TSymFlags): PSym =
var rd: PRodReader = nil var rd: PRodReader = nil
var f = addFileExt(filename, nimExt) var f = addFileExt(filename, nimExt)
result = newModule(filename) result = newModule(filename)
if isMainFile: incl(result.flags, sfMainModule) result.flags = result.flags + flags
if isSystemFile: incl(result.flags, sfSystemModule) if gCmd in {cmdCompileToC, cmdCompileToCpp}:
if (gCmd == cmdCompileToC) or (gCmd == cmdCompileToCpp):
rd = handleSymbolFile(result, f) rd = handleSymbolFile(result, f)
if result.id < 0: if result.id < 0:
InternalError("handleSymbolFile should have set the module\'s ID") InternalError("handleSymbolFile should have set the module\'s ID")
@ -90,9 +82,9 @@ proc CompileModule(filename: string, isMainFile, isSystemFile: bool): PSym =
processModule(result, f, nil, rd) processModule(result, f, nil, rd)
proc CompileProject(filename: string) = proc CompileProject(filename: string) =
discard CompileModule(JoinPath(options.libpath, addFileExt("system", nimExt)), discard CompileModule(options.libpath / addFileExt("system", nimExt),
false, true) {sfSystemModule})
discard CompileModule(addFileExt(filename, nimExt), true, false) discard CompileModule(addFileExt(filename, nimExt), {sfMainModule})
proc semanticPasses() = proc semanticPasses() =
registerPass(verbosePass()) registerPass(verbosePass())
@ -147,8 +139,8 @@ proc CommandInteractive() =
registerPass(verbosePass()) registerPass(verbosePass())
registerPass(sem.semPass()) registerPass(sem.semPass())
registerPass(evals.evalPass()) # load system module: registerPass(evals.evalPass()) # load system module:
discard CompileModule(JoinPath(options.libpath, addFileExt("system", nimExt)), discard CompileModule(options.libpath / addFileExt("system", nimExt),
false, true) {sfSystemModule})
var m = newModule("stdin") var m = newModule("stdin")
m.id = getID() m.id = getID()
incl(m.flags, sfMainModule) incl(m.flags, sfMainModule)
@ -232,7 +224,7 @@ proc MainCommand(cmd, filename: string) =
rawMessage(errInvalidCommandX, cmd) rawMessage(errInvalidCommandX, cmd)
of "pretty": of "pretty":
gCmd = cmdPretty gCmd = cmdPretty
wantFile(filename) #CommandExportSymbols(filename); wantFile(filename)
CommandPretty(filename) CommandPretty(filename)
of "doc": of "doc":
gCmd = cmdDoc gCmd = cmdDoc

View file

@ -76,6 +76,7 @@ type
errNamedExprNotAllowed, errXExpectsOneTypeParam, errNamedExprNotAllowed, errXExpectsOneTypeParam,
errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed, errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed,
errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType, errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType,
errNoReturnTypeDeclared,
errInvalidCommandX, errXOnlyAtModuleScope, errInvalidCommandX, errXOnlyAtModuleScope,
errXNeedsParamObjectType, errXNeedsParamObjectType,
errTemplateInstantiationTooNested, errInstantiationFrom, errTemplateInstantiationTooNested, errInstantiationFrom,
@ -284,6 +285,7 @@ const
errXCannotBeAssignedTo: "\'$1\' cannot be assigned to", errXCannotBeAssignedTo: "\'$1\' cannot be assigned to",
errIteratorNotAllowed: "iterators can only be defined at the module\'s top level", errIteratorNotAllowed: "iterators can only be defined at the module\'s top level",
errXNeedsReturnType: "$1 needs a return type", errXNeedsReturnType: "$1 needs a return type",
errNoReturnTypeDeclared: "no return type declared",
errInvalidCommandX: "invalid command: \'$1\'", errInvalidCommandX: "invalid command: \'$1\'",
errXOnlyAtModuleScope: "\'$1\' is only allowed at top level", errXOnlyAtModuleScope: "\'$1\' is only allowed at top level",
errXNeedsParamObjectType: "'$1' needs a parameter that has an object type", errXNeedsParamObjectType: "'$1' needs a parameter that has an object type",

View file

@ -10,7 +10,7 @@
## implements some little helper passes ## implements some little helper passes
import import
strutils, ast, astalgo, passes, msgs, options strutils, ast, astalgo, passes, msgs, options, idgen
proc verboseOpen(s: PSym, filename: string): PPassContext = proc verboseOpen(s: PSym, filename: string): PPassContext =
#MessageOut('compiling ' + s.name.s); #MessageOut('compiling ' + s.name.s);
@ -24,7 +24,7 @@ proc verboseProcess(context: PPassContext, n: PNode): PNode =
# system.nim deactivates all hints, for verbosity:3 we want the processing # system.nim deactivates all hints, for verbosity:3 we want the processing
# messages nonetheless, so we activate them again unconditionally: # messages nonetheless, so we activate them again unconditionally:
incl(msgs.gNotes, hintProcessing) incl(msgs.gNotes, hintProcessing)
Message(n.info, hintProcessing, $ast.gid) Message(n.info, hintProcessing, $idgen.gBackendId)
proc verbosePass*(): TPass = proc verbosePass*(): TPass =
initPass(result) initPass(result)

View file

@ -13,7 +13,7 @@
import import
strutils, lists, options, ast, astalgo, llstream, msgs, platform, os, strutils, lists, options, ast, astalgo, llstream, msgs, platform, os,
condsyms, idents, renderer, types, extccomp, math, magicsys, nversion, condsyms, idents, renderer, types, extccomp, math, magicsys, nversion,
nimsets, syntaxes, times, rodread, semthreads nimsets, syntaxes, times, rodread, semthreads, idgen
type type
TPassContext* = object of TObject # the pass's context TPassContext* = object of TObject # the pass's context

View file

@ -73,7 +73,7 @@
import import
os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms, os, options, strutils, nversion, ast, astalgo, msgs, platform, condsyms,
ropes, idents, crc ropes, idents, crc, idgen, rodutils
type type
TReasonForRecompile* = enum TReasonForRecompile* = enum
@ -100,12 +100,12 @@ type
TIndex*{.final.} = object # an index with compression TIndex*{.final.} = object # an index with compression
lastIdxKey*, lastIdxVal*: int lastIdxKey*, lastIdxVal*: int
tab*: TIITable tab*: TIITable
r*: PRope # writers use this r*: string # writers use this
offset*: int # readers use this offset*: int # readers use this
TRodReader* = object of TObject TRodReader* = object of TObject
pos*: int # position; used for parsing pos*: int # position; used for parsing
s*: string # the whole file in memory s*: string # the whole file in memory; XXX mmap this!
options*: TOptions options*: TOptions
reason*: TReasonForRecompile reason*: TReasonForRecompile
modDeps*: TStringSeq modDeps*: TStringSeq
@ -132,8 +132,7 @@ proc handleSymbolFile*(module: PSym, filename: string): PRodReader
proc GetCRC*(filename: string): TCrc32 proc GetCRC*(filename: string): TCrc32
proc loadInitSection*(r: PRodReader): PNode proc loadInitSection*(r: PRodReader): PNode
proc loadStub*(s: PSym) proc loadStub*(s: PSym)
proc encodeInt*(x: BiggestInt): PRope
proc encode*(s: string): PRope
# implementation # implementation
var gTypeTable: TIdTable var gTypeTable: TIdTable
@ -141,52 +140,19 @@ var gTypeTable: TIdTable
proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym
# `info` is only used for debugging purposes # `info` is only used for debugging purposes
proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType proc rrGetType(r: PRodReader, id: int, info: TLineInfo): PType
proc decode(r: PRodReader): string
proc decodeInt(r: PRodReader): int
proc decodeBInt(r: PRodReader): biggestInt
proc encode(s: string): PRope =
var res = ""
for i in countup(0, len(s) - 1):
case s[i]
of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(res, s[i])
else: add(res, '\\' & toHex(ord(s[i]), 2))
result = toRope(res)
proc encodeIntAux(str: var string, x: BiggestInt) =
const chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
var d: char
var v = x
var rem: biggestInt = v mod 190
if (rem < 0):
add(str, '-')
v = - (v div 190)
rem = - rem
else:
v = v div 190
var idx = int(rem)
if idx < 62: d = chars[idx + 0]
else: d = chr(idx - 62 + 128)
if (v != 0): encodeIntAux(str, v)
add(str, d)
proc encodeInt(x: BiggestInt): PRope =
var res = ""
encodeIntAux(res, x)
result = toRope(res)
proc decodeLineInfo(r: PRodReader, info: var TLineInfo) = proc decodeLineInfo(r: PRodReader, info: var TLineInfo) =
if r.s[r.pos] == '?': if r.s[r.pos] == '?':
inc(r.pos) inc(r.pos)
if r.s[r.pos] == ',': info.col = int16(- 1) if r.s[r.pos] == ',': info.col = int16(- 1)
else: info.col = int16(decodeInt(r)) else: info.col = int16(decodeVInt(r.s, r.pos))
if r.s[r.pos] == ',': if r.s[r.pos] == ',':
inc(r.pos) inc(r.pos)
if r.s[r.pos] == ',': info.line = int16(- 1) if r.s[r.pos] == ',': info.line = int16(- 1)
else: info.line = int16(decodeInt(r)) else: info.line = int16(decodeVInt(r.s, r.pos))
if r.s[r.pos] == ',': if r.s[r.pos] == ',':
inc(r.pos) inc(r.pos)
info = newLineInfo(r.files[decodeInt(r)], info.line, info.col) info = newLineInfo(r.files[decodeVInt(r.s, r.pos)], info.line, info.col)
proc decodeNode(r: PRodReader, fInfo: TLineInfo): PNode = proc decodeNode(r: PRodReader, fInfo: TLineInfo): PNode =
result = nil result = nil
@ -195,42 +161,42 @@ proc decodeNode(r: PRodReader, fInfo: TLineInfo): PNode =
if r.s[r.pos] == ')': if r.s[r.pos] == ')':
inc(r.pos) inc(r.pos)
return # nil node return # nil node
result = newNodeI(TNodeKind(decodeInt(r)), fInfo) result = newNodeI(TNodeKind(decodeVInt(r.s, r.pos)), fInfo)
decodeLineInfo(r, result.info) decodeLineInfo(r, result.info)
if r.s[r.pos] == '$': if r.s[r.pos] == '$':
inc(r.pos) inc(r.pos)
result.flags = cast[TNodeFlags](int32(decodeInt(r))) result.flags = cast[TNodeFlags](int32(decodeVInt(r.s, r.pos)))
if r.s[r.pos] == '^': if r.s[r.pos] == '^':
inc(r.pos) inc(r.pos)
var id = decodeInt(r) var id = decodeVInt(r.s, r.pos)
result.typ = rrGetType(r, id, result.info) result.typ = rrGetType(r, id, result.info)
case result.kind case result.kind
of nkCharLit..nkInt64Lit: of nkCharLit..nkInt64Lit:
if r.s[r.pos] == '!': if r.s[r.pos] == '!':
inc(r.pos) inc(r.pos)
result.intVal = decodeBInt(r) result.intVal = decodeVBiggestInt(r.s, r.pos)
of nkFloatLit..nkFloat64Lit: of nkFloatLit..nkFloat64Lit:
if r.s[r.pos] == '!': if r.s[r.pos] == '!':
inc(r.pos) inc(r.pos)
var fl = decode(r) var fl = decodeStr(r.s, r.pos)
result.floatVal = parseFloat(fl) result.floatVal = parseFloat(fl)
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
if r.s[r.pos] == '!': if r.s[r.pos] == '!':
inc(r.pos) inc(r.pos)
result.strVal = decode(r) result.strVal = decodeStr(r.s, r.pos)
else: else:
result.strVal = "" # BUGFIX result.strVal = "" # BUGFIX
of nkIdent: of nkIdent:
if r.s[r.pos] == '!': if r.s[r.pos] == '!':
inc(r.pos) inc(r.pos)
var fl = decode(r) var fl = decodeStr(r.s, r.pos)
result.ident = getIdent(fl) result.ident = getIdent(fl)
else: else:
internalError(result.info, "decodeNode: nkIdent") internalError(result.info, "decodeNode: nkIdent")
of nkSym: of nkSym:
if r.s[r.pos] == '!': if r.s[r.pos] == '!':
inc(r.pos) inc(r.pos)
var id = decodeInt(r) var id = decodeVInt(r.s, r.pos)
result.sym = rrGetSym(r, id, result.info) result.sym = rrGetSym(r, id, result.info)
else: else:
internalError(result.info, "decodeNode: nkSym") internalError(result.info, "decodeNode: nkSym")
@ -245,32 +211,32 @@ proc decodeLoc(r: PRodReader, loc: var TLoc, info: TLineInfo) =
if r.s[r.pos] == '<': if r.s[r.pos] == '<':
inc(r.pos) inc(r.pos)
if r.s[r.pos] in {'0'..'9', 'a'..'z', 'A'..'Z'}: if r.s[r.pos] in {'0'..'9', 'a'..'z', 'A'..'Z'}:
loc.k = TLocKind(decodeInt(r)) loc.k = TLocKind(decodeVInt(r.s, r.pos))
else: else:
loc.k = low(loc.k) loc.k = low(loc.k)
if r.s[r.pos] == '*': if r.s[r.pos] == '*':
inc(r.pos) inc(r.pos)
loc.s = TStorageLoc(decodeInt(r)) loc.s = TStorageLoc(decodeVInt(r.s, r.pos))
else: else:
loc.s = low(loc.s) loc.s = low(loc.s)
if r.s[r.pos] == '$': if r.s[r.pos] == '$':
inc(r.pos) inc(r.pos)
loc.flags = cast[TLocFlags](int32(decodeInt(r))) loc.flags = cast[TLocFlags](int32(decodeVInt(r.s, r.pos)))
else: else:
loc.flags = {} loc.flags = {}
if r.s[r.pos] == '^': if r.s[r.pos] == '^':
inc(r.pos) inc(r.pos)
loc.t = rrGetType(r, decodeInt(r), info) loc.t = rrGetType(r, decodeVInt(r.s, r.pos), info)
else: else:
loc.t = nil loc.t = nil
if r.s[r.pos] == '!': if r.s[r.pos] == '!':
inc(r.pos) inc(r.pos)
loc.r = toRope(decode(r)) loc.r = toRope(decodeStr(r.s, r.pos))
else: else:
loc.r = nil loc.r = nil
if r.s[r.pos] == '?': if r.s[r.pos] == '?':
inc(r.pos) inc(r.pos)
loc.a = decodeInt(r) loc.a = decodeVInt(r.s, r.pos)
else: else:
loc.a = 0 loc.a = 0
if r.s[r.pos] == '>': inc(r.pos) if r.s[r.pos] == '>': inc(r.pos)
@ -284,10 +250,10 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType =
inc(r.pos) inc(r.pos)
return # nil type return # nil type
new(result) new(result)
result.kind = TTypeKind(decodeInt(r)) result.kind = TTypeKind(decodeVInt(r.s, r.pos))
if r.s[r.pos] == '+': if r.s[r.pos] == '+':
inc(r.pos) inc(r.pos)
result.id = decodeInt(r) result.id = decodeVInt(r.s, r.pos)
setId(result.id) setId(result.id)
if debugIds: registerID(result) if debugIds: registerID(result)
else: else:
@ -297,29 +263,29 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType =
if r.s[r.pos] == '(': result.n = decodeNode(r, UnknownLineInfo()) if r.s[r.pos] == '(': result.n = decodeNode(r, UnknownLineInfo())
if r.s[r.pos] == '$': if r.s[r.pos] == '$':
inc(r.pos) inc(r.pos)
result.flags = cast[TTypeFlags](int32(decodeInt(r))) result.flags = cast[TTypeFlags](int32(decodeVInt(r.s, r.pos)))
if r.s[r.pos] == '?': if r.s[r.pos] == '?':
inc(r.pos) inc(r.pos)
result.callConv = TCallingConvention(decodeInt(r)) result.callConv = TCallingConvention(decodeVInt(r.s, r.pos))
if r.s[r.pos] == '*': if r.s[r.pos] == '*':
inc(r.pos) inc(r.pos)
result.owner = rrGetSym(r, decodeInt(r), info) result.owner = rrGetSym(r, decodeVInt(r.s, r.pos), info)
if r.s[r.pos] == '&': if r.s[r.pos] == '&':
inc(r.pos) inc(r.pos)
result.sym = rrGetSym(r, decodeInt(r), info) result.sym = rrGetSym(r, decodeVInt(r.s, r.pos), info)
if r.s[r.pos] == '/': if r.s[r.pos] == '/':
inc(r.pos) inc(r.pos)
result.size = decodeInt(r) result.size = decodeVInt(r.s, r.pos)
else: else:
result.size = - 1 result.size = - 1
if r.s[r.pos] == '=': if r.s[r.pos] == '=':
inc(r.pos) inc(r.pos)
result.align = decodeInt(r) result.align = decodeVInt(r.s, r.pos)
else: else:
result.align = 2 result.align = 2
if r.s[r.pos] == '@': if r.s[r.pos] == '@':
inc(r.pos) inc(r.pos)
result.containerID = decodeInt(r) result.containerID = decodeVInt(r.s, r.pos)
decodeLoc(r, result.loc, info) decodeLoc(r, result.loc, info)
while r.s[r.pos] == '^': while r.s[r.pos] == '^':
inc(r.pos) inc(r.pos)
@ -329,7 +295,7 @@ proc decodeType(r: PRodReader, info: TLineInfo): PType =
else: InternalError(info, "decodeType ^(" & r.s[r.pos]) else: InternalError(info, "decodeType ^(" & r.s[r.pos])
addSon(result, nil) addSon(result, nil)
else: else:
var d = decodeInt(r) var d = decodeVInt(r.s, r.pos)
addSon(result, rrGetType(r, d, info)) addSon(result, rrGetType(r, d, info))
proc decodeLib(r: PRodReader, info: TLineInfo): PLib = proc decodeLib(r: PRodReader, info: TLineInfo): PLib =
@ -337,10 +303,10 @@ proc decodeLib(r: PRodReader, info: TLineInfo): PLib =
if r.s[r.pos] == '|': if r.s[r.pos] == '|':
new(result) new(result)
inc(r.pos) inc(r.pos)
result.kind = TLibKind(decodeInt(r)) result.kind = TLibKind(decodeVInt(r.s, r.pos))
if r.s[r.pos] != '|': InternalError("decodeLib: 1") if r.s[r.pos] != '|': InternalError("decodeLib: 1")
inc(r.pos) inc(r.pos)
result.name = toRope(decode(r)) result.name = toRope(decodeStr(r.s, r.pos))
if r.s[r.pos] != '|': InternalError("decodeLib: 2") if r.s[r.pos] != '|': InternalError("decodeLib: 2")
inc(r.pos) inc(r.pos)
result.path = decodeNode(r, info) result.path = decodeNode(r, info)
@ -355,16 +321,16 @@ proc decodeSym(r: PRodReader, info: TLineInfo): PSym =
if r.s[r.pos] == '}': if r.s[r.pos] == '}':
inc(r.pos) inc(r.pos)
return # nil sym return # nil sym
var k = TSymKind(decodeInt(r)) var k = TSymKind(decodeVInt(r.s, r.pos))
if r.s[r.pos] == '+': if r.s[r.pos] == '+':
inc(r.pos) inc(r.pos)
id = decodeInt(r) id = decodeVInt(r.s, r.pos)
setId(id) setId(id)
else: else:
InternalError(info, "decodeSym: no id") InternalError(info, "decodeSym: no id")
if r.s[r.pos] == '&': if r.s[r.pos] == '&':
inc(r.pos) inc(r.pos)
ident = getIdent(decode(r)) ident = getIdent(decodeStr(r.s, r.pos))
else: else:
InternalError(info, "decodeSym: no ident") InternalError(info, "decodeSym: no ident")
result = PSym(IdTableGet(r.syms, id)) result = PSym(IdTableGet(r.syms, id))
@ -379,100 +345,37 @@ proc decodeSym(r: PRodReader, info: TLineInfo): PSym =
result.name = ident # read the rest of the symbol description: result.name = ident # read the rest of the symbol description:
if r.s[r.pos] == '^': if r.s[r.pos] == '^':
inc(r.pos) inc(r.pos)
result.typ = rrGetType(r, decodeInt(r), info) result.typ = rrGetType(r, decodeVInt(r.s, r.pos), info)
decodeLineInfo(r, result.info) decodeLineInfo(r, result.info)
if r.s[r.pos] == '*': if r.s[r.pos] == '*':
inc(r.pos) inc(r.pos)
result.owner = rrGetSym(r, decodeInt(r), result.info) result.owner = rrGetSym(r, decodeVInt(r.s, r.pos), result.info)
if r.s[r.pos] == '$': if r.s[r.pos] == '$':
inc(r.pos) inc(r.pos)
result.flags = cast[TSymFlags](int32(decodeInt(r))) result.flags = cast[TSymFlags](int32(decodeVInt(r.s, r.pos)))
if r.s[r.pos] == '@': if r.s[r.pos] == '@':
inc(r.pos) inc(r.pos)
result.magic = TMagic(decodeInt(r)) result.magic = TMagic(decodeVInt(r.s, r.pos))
if r.s[r.pos] == '(': result.ast = decodeNode(r, result.info) if r.s[r.pos] == '(': result.ast = decodeNode(r, result.info)
if r.s[r.pos] == '!': if r.s[r.pos] == '!':
inc(r.pos) inc(r.pos)
result.options = cast[TOptions](int32(decodeInt(r))) result.options = cast[TOptions](int32(decodeVInt(r.s, r.pos)))
else: else:
result.options = r.options result.options = r.options
if r.s[r.pos] == '%': if r.s[r.pos] == '%':
inc(r.pos) inc(r.pos)
result.position = decodeInt(r) result.position = decodeVInt(r.s, r.pos)
else: else:
result.position = 0 result.position = 0
# BUGFIX: this may have been misused as reader index! # BUGFIX: this may have been misused as reader index!
if r.s[r.pos] == '`': if r.s[r.pos] == '`':
inc(r.pos) inc(r.pos)
result.offset = decodeInt(r) result.offset = decodeVInt(r.s, r.pos)
else: else:
result.offset = - 1 result.offset = - 1
decodeLoc(r, result.loc, result.info) decodeLoc(r, result.loc, result.info)
result.annex = decodeLib(r, info) result.annex = decodeLib(r, info)
proc decodeInt(r: PRodReader): int =
# base 190 numbers
var i = r.pos
var sign = - 1
assert(r.s[i] in {'a'..'z', 'A'..'Z', '0'..'9', '-', '\x80'..'\xFF'})
if r.s[i] == '-':
inc(i)
sign = 1
result = 0
while true:
case r.s[i]
of '0'..'9': result = result * 190 - (ord(r.s[i]) - ord('0'))
of 'a'..'z': result = result * 190 - (ord(r.s[i]) - ord('a') + 10)
of 'A'..'Z': result = result * 190 - (ord(r.s[i]) - ord('A') + 36)
of '\x80'..'\xFF': result = result * 190 - (ord(r.s[i]) - 128 + 62)
else: break
inc(i)
result = result * sign
r.pos = i
proc decodeBInt(r: PRodReader): biggestInt =
var i = r.pos
var sign: biggestInt = - 1
assert(r.s[i] in {'a'..'z', 'A'..'Z', '0'..'9', '-', '\x80'..'\xFF'})
if r.s[i] == '-':
inc(i)
sign = 1
result = 0
while true:
case r.s[i]
of '0'..'9': result = result * 190 - (ord(r.s[i]) - ord('0'))
of 'a'..'z': result = result * 190 - (ord(r.s[i]) - ord('a') + 10)
of 'A'..'Z': result = result * 190 - (ord(r.s[i]) - ord('A') + 36)
of '\x80'..'\xFF': result = result * 190 - (ord(r.s[i]) - 128 + 62)
else: break
inc(i)
result = result * sign
r.pos = i
proc hexChar(c: char, xi: var int) =
case c
of '0'..'9': xi = (xi shl 4) or (ord(c) - ord('0'))
of 'a'..'f': xi = (xi shl 4) or (ord(c) - ord('a') + 10)
of 'A'..'F': xi = (xi shl 4) or (ord(c) - ord('A') + 10)
else: nil
proc decode(r: PRodReader): string =
var i = r.pos
result = ""
while true:
case r.s[i]
of '\\':
inc(i, 3)
var xi = 0
hexChar(r.s[i-2], xi)
hexChar(r.s[i-1], xi)
add(result, chr(xi))
of 'a'..'z', 'A'..'Z', '0'..'9', '_':
add(result, r.s[i])
inc(i)
else: break
r.pos = i
proc skipSection(r: PRodReader) = proc skipSection(r: PRodReader) =
if r.s[r.pos] == ':': if r.s[r.pos] == ':':
while r.s[r.pos] > '\x0A': inc(r.pos) while r.s[r.pos] > '\x0A': inc(r.pos)
@ -514,9 +417,9 @@ proc processInterf(r: PRodReader, module: PSym) =
if r.interfIdx == 0: InternalError("processInterf") if r.interfIdx == 0: InternalError("processInterf")
r.pos = r.interfIdx r.pos = r.interfIdx
while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'): while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
var w = decode(r) var w = decodeStr(r.s, r.pos)
inc(r.pos) inc(r.pos)
var key = decodeInt(r) var key = decodeVInt(r.s, r.pos)
inc(r.pos) # #10 inc(r.pos) # #10
var s = newStub(r, w, key) var s = newStub(r, w, key)
s.owner = module s.owner = module
@ -527,9 +430,9 @@ proc processCompilerProcs(r: PRodReader, module: PSym) =
if r.compilerProcsIdx == 0: InternalError("processCompilerProcs") if r.compilerProcsIdx == 0: InternalError("processCompilerProcs")
r.pos = r.compilerProcsIdx r.pos = r.compilerProcsIdx
while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'): while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
var w = decode(r) var w = decodeStr(r.s, r.pos)
inc(r.pos) inc(r.pos)
var key = decodeInt(r) var key = decodeVInt(r.s, r.pos)
inc(r.pos) # #10 inc(r.pos) # #10
var s = PSym(IdTableGet(r.syms, key)) var s = PSym(IdTableGet(r.syms, key))
if s == nil: if s == nil:
@ -543,11 +446,11 @@ proc processIndex(r: PRodReader, idx: var TIndex) =
inc(r.pos, 2) # skip "(\10" inc(r.pos, 2) # skip "(\10"
inc(r.line) inc(r.line)
while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'): while (r.s[r.pos] > '\x0A') and (r.s[r.pos] != ')'):
tmp = decodeInt(r) tmp = decodeVInt(r.s, r.pos)
if r.s[r.pos] == ' ': if r.s[r.pos] == ' ':
inc(r.pos) inc(r.pos)
key = idx.lastIdxKey + tmp key = idx.lastIdxKey + tmp
val = decodeInt(r) + idx.lastIdxVal val = decodeVInt(r.s, r.pos) + idx.lastIdxVal
else: else:
key = idx.lastIdxKey + 1 key = idx.lastIdxKey + 1
val = tmp + idx.lastIdxVal val = tmp + idx.lastIdxVal
@ -571,20 +474,20 @@ proc processRodFile(r: PRodReader, crc: TCrc32) =
case section case section
of "CRC": of "CRC":
inc(r.pos) # skip ':' inc(r.pos) # skip ':'
if int(crc) != decodeInt(r): r.reason = rrCrcChange if int(crc) != decodeVInt(r.s, r.pos): r.reason = rrCrcChange
of "ID": of "ID":
inc(r.pos) # skip ':' inc(r.pos) # skip ':'
r.moduleID = decodeInt(r) r.moduleID = decodeVInt(r.s, r.pos)
setID(r.moduleID) setID(r.moduleID)
of "OPTIONS": of "OPTIONS":
inc(r.pos) # skip ':' inc(r.pos) # skip ':'
r.options = cast[TOptions](int32(decodeInt(r))) r.options = cast[TOptions](int32(decodeVInt(r.s, r.pos)))
if options.gOptions != r.options: r.reason = rrOptions if options.gOptions != r.options: r.reason = rrOptions
of "DEFINES": of "DEFINES":
inc(r.pos) # skip ':' inc(r.pos) # skip ':'
d = 0 d = 0
while r.s[r.pos] > '\x0A': while r.s[r.pos] > '\x0A':
w = decode(r) w = decodeStr(r.s, r.pos)
inc(d) inc(d)
if not condsyms.isDefined(getIdent(w)): if not condsyms.isDefined(getIdent(w)):
r.reason = rrDefines #MessageOut('not defined, but should: ' + w); r.reason = rrDefines #MessageOut('not defined, but should: ' + w);
@ -596,7 +499,7 @@ proc processRodFile(r: PRodReader, crc: TCrc32) =
L = 0 L = 0
while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')': while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')':
setlen(r.files, L + 1) setlen(r.files, L + 1)
r.files[L] = decode(r) r.files[L] = decodeStr(r.s, r.pos)
inc(r.pos) # skip #10 inc(r.pos) # skip #10
inc(r.line) inc(r.line)
inc(L) inc(L)
@ -605,9 +508,9 @@ proc processRodFile(r: PRodReader, crc: TCrc32) =
inc(r.pos, 2) # skip "(\10" inc(r.pos, 2) # skip "(\10"
inc(r.line) inc(r.line)
while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')': while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')':
w = r.files[decodeInt(r)] w = r.files[decodeVInt(r.s, r.pos)]
inc(r.pos) # skip ' ' inc(r.pos) # skip ' '
inclCrc = decodeInt(r) inclCrc = decodeVInt(r.s, r.pos)
if r.reason == rrNone: if r.reason == rrNone:
if not ExistsFile(w) or (inclCrc != int(crcFromFile(w))): if not ExistsFile(w) or (inclCrc != int(crcFromFile(w))):
r.reason = rrInclDeps r.reason = rrInclDeps
@ -620,7 +523,7 @@ proc processRodFile(r: PRodReader, crc: TCrc32) =
L = 0 L = 0
while r.s[r.pos] > '\x0A': while r.s[r.pos] > '\x0A':
setlen(r.modDeps, L + 1) setlen(r.modDeps, L + 1)
r.modDeps[L] = r.files[decodeInt(r)] r.modDeps[L] = r.files[decodeVInt(r.s, r.pos)]
inc(L) inc(L)
if r.s[r.pos] == ' ': inc(r.pos) if r.s[r.pos] == ' ': inc(r.pos)
of "INTERF": of "INTERF":
@ -723,8 +626,9 @@ proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym =
if d == invalidKey: if d == invalidKey:
var moduleID = IiTableGet(r.imports.tab, id) var moduleID = IiTableGet(r.imports.tab, id)
if moduleID < 0: if moduleID < 0:
InternalError(info, var x = ""
"missing from both indexes: +" & ropeToStr(encodeInt(id))) encodeVInt(id, x)
InternalError(info, "missing from both indexes: +" & x)
# find the reader with the correct moduleID: # find the reader with the correct moduleID:
for i in countup(0, high(gMods)): for i in countup(0, high(gMods)):
var rd = gMods[i].rd var rd = gMods[i].rd
@ -735,8 +639,9 @@ proc rrGetSym(r: PRodReader, id: int, info: TLineInfo): PSym =
result = decodeSymSafePos(rd, d, info) result = decodeSymSafePos(rd, d, info)
break break
else: else:
InternalError(info, "rrGetSym: no reader found: +" & var x = ""
ropeToStr(encodeInt(id))) encodeVInt(id, x)
InternalError(info, "rrGetSym: no reader found: +" & x)
else: else:
#if IiTableGet(rd.index.tab, id) <> invalidKey then #if IiTableGet(rd.index.tab, id) <> invalidKey then
# XXX expensive check! # XXX expensive check!
@ -753,7 +658,7 @@ proc loadInitSection(r: PRodReader): PNode =
r.pos = r.initIdx r.pos = r.initIdx
result = newNode(nkStmtList) result = newNode(nkStmtList)
while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')': while r.s[r.pos] > '\x0A' and r.s[r.pos] != ')':
var d = decodeInt(r) var d = decodeVInt(r.s, r.pos)
inc(r.pos) # #10 inc(r.pos) # #10
var p = r.pos var p = r.pos
r.pos = d + r.dataIdx r.pos = d + r.dataIdx
@ -767,7 +672,7 @@ proc loadConverters(r: PRodReader) =
InternalError("importConverters") InternalError("importConverters")
r.pos = r.convertersIdx r.pos = r.convertersIdx
while (r.s[r.pos] > '\x0A'): while (r.s[r.pos] > '\x0A'):
var d = decodeInt(r) var d = decodeVInt(r.s, r.pos)
discard rrGetSym(r, d, UnknownLineInfo()) discard rrGetSym(r, d, UnknownLineInfo())
if r.s[r.pos] == ' ': inc(r.pos) if r.s[r.pos] == ' ': inc(r.pos)
@ -818,9 +723,11 @@ proc checkDep(filename: string): TReasonForRecompile =
gMods[idx].reason = result # now we know better gMods[idx].reason = result # now we know better
proc handleSymbolFile(module: PSym, filename: string): PRodReader = proc handleSymbolFile(module: PSym, filename: string): PRodReader =
if not (optSymbolFiles in gGlobalOptions): if optSymbolFiles notin gGlobalOptions:
module.id = getID() module.id = getID()
return nil return nil
idgen.loadMaxIds(options.projectPath / options.projectName)
discard checkDep(filename) discard checkDep(filename)
var idx = getModuleIdx(filename) var idx = getModuleIdx(filename)
if gMods[idx].reason == rrEmpty: InternalError("handleSymbolFile") if gMods[idx].reason == rrEmpty: InternalError("handleSymbolFile")

View file

@ -7,8 +7,8 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
## Utilities for the compiler. Aim is to reduce the coupling between ## Serialization utilities for the compiler.
## the compiler and the evolving stdlib. import strutils
proc c_sprintf(buf, frmt: cstring) {.importc: "sprintf", nodecl, varargs.} proc c_sprintf(buf, frmt: cstring) {.importc: "sprintf", nodecl, varargs.}
@ -25,3 +25,97 @@ proc ToStrMaxPrecision*(f: BiggestFloat): string =
c_sprintf(buf, "%#.16e", f) c_sprintf(buf, "%#.16e", f)
result = $buf result = $buf
proc encodeStr*(s: string, result: var string) =
for i in countup(0, len(s) - 1):
case s[i]
of 'a'..'z', 'A'..'Z', '0'..'9', '_': add(result, s[i])
else: add(result, '\\' & toHex(ord(s[i]), 2))
proc hexChar(c: char, xi: var int) =
case c
of '0'..'9': xi = (xi shl 4) or (ord(c) - ord('0'))
of 'a'..'f': xi = (xi shl 4) or (ord(c) - ord('a') + 10)
of 'A'..'F': xi = (xi shl 4) or (ord(c) - ord('A') + 10)
else: nil
proc decodeStr*(s: cstring, pos: var int): string =
var i = pos
result = ""
while true:
case s[i]
of '\\':
inc(i, 3)
var xi = 0
hexChar(s[i-2], xi)
hexChar(s[i-1], xi)
add(result, chr(xi))
of 'a'..'z', 'A'..'Z', '0'..'9', '_':
add(result, s[i])
inc(i)
else: break
pos = i
const
chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
template encodeIntImpl(self: expr) =
var d: char
var v = x
var rem = v mod 190
if rem < 0:
add(result, '-')
v = - (v div 190)
rem = - rem
else:
v = v div 190
var idx = int(rem)
if idx < 62: d = chars[idx]
else: d = chr(idx - 62 + 128)
if v != 0: self(v, result)
add(result, d)
proc encodeVBiggestInt*(x: BiggestInt, result: var string) =
## encode a biggest int as a variable length base 190 int.
encodeIntImpl(encodeVBiggestInt)
proc encodeVInt*(x: int, result: var string) =
## encode an int as a variable length base 190 int.
encodeIntImpl(encodeVInt)
template decodeIntImpl() =
var i = pos
var sign = - 1
assert(s[i] in {'a'..'z', 'A'..'Z', '0'..'9', '-', '\x80'..'\xFF'})
if s[i] == '-':
inc(i)
sign = 1
result = 0
while true:
case s[i]
of '0'..'9': result = result * 190 - (ord(s[i]) - ord('0'))
of 'a'..'z': result = result * 190 - (ord(s[i]) - ord('a') + 10)
of 'A'..'Z': result = result * 190 - (ord(s[i]) - ord('A') + 36)
of '\x80'..'\xFF': result = result * 190 - (ord(s[i]) - 128 + 62)
else: break
inc(i)
result = result * sign
pos = i
proc decodeVInt*(s: cstring, pos: var int): int =
decodeIntImpl()
proc decodeVBiggestInt*(s: cstring, pos: var int): biggestInt =
decodeIntImpl()
iterator decodeVIntArray*(s: cstring): int =
var i = 0
while s[i] != '\0':
yield decodeVInt(s, i)
if s[i] == ' ': inc i
iterator decodeStrArray*(s: cstring): string =
var i = 0
while s[i] != '\0':
yield decodeStr(s, i)
if s[i] == ' ': inc i

View file

@ -1,7 +1,7 @@
# #
# #
# The Nimrod Compiler # The Nimrod Compiler
# (c) Copyright 2008 Andreas Rumpf # (c) Copyright 2011 Andreas Rumpf
# #
# See the file "copying.txt", included in this # See the file "copying.txt", included in this
# distribution, for details about the copyright. # distribution, for details about the copyright.
@ -13,29 +13,29 @@
import import
intsets, os, options, strutils, nversion, ast, astalgo, msgs, platform, intsets, os, options, strutils, nversion, ast, astalgo, msgs, platform,
condsyms, ropes, idents, crc, rodread, passes, importer condsyms, ropes, idents, crc, rodread, passes, importer, idgen, rodutils
proc rodwritePass*(): TPass proc rodwritePass*(): TPass
# implementation # implementation
type type
TRodWriter = object of TPassContext TRodWriter = object of TPassContext
module*: PSym module: PSym
crc*: TCrc32 crc: TCrc32
options*: TOptions options: TOptions
defines*: PRope defines: string
inclDeps*: PRope inclDeps: string
modDeps*: PRope modDeps: string
interf*: PRope interf: string
compilerProcs*: PRope compilerProcs: string
index*, imports*: TIndex index, imports: TIndex
converters*: PRope converters: string
init*: PRope init: string
data*: PRope data: string
filename*: string filename: string
sstack*: TSymSeq # a stack of symbols to process sstack: TSymSeq # a stack of symbols to process
tstack*: TTypeSeq # a stack of types to process tstack: TTypeSeq # a stack of types to process
files*: TStringSeq files: TStringSeq
PRodWriter = ref TRodWriter PRodWriter = ref TRodWriter
@ -45,19 +45,17 @@ proc addInclDep(w: PRodWriter, dep: string)
proc addInterfaceSym(w: PRodWriter, s: PSym) proc addInterfaceSym(w: PRodWriter, s: PSym)
proc addStmt(w: PRodWriter, n: PNode) proc addStmt(w: PRodWriter, n: PNode)
proc writeRod(w: PRodWriter) proc writeRod(w: PRodWriter)
proc encodeStr(w: PRodWriter, s: string): PRope =
result = encode(s)
proc processStacks(w: PRodWriter) proc processStacks(w: PRodWriter)
proc getDefines(): PRope = proc getDefines(): string =
var it: TTabIter var it: TTabIter
var s = InitTabIter(it, gSymbols) var s = InitTabIter(it, gSymbols)
result = nil result = ""
while s != nil: while s != nil:
if s.position == 1: if s.position == 1:
if result != nil: app(result, " ") if result.len != 0: add(result, " ")
app(result, s.name.s) add(result, s.name.s)
s = nextIter(it, gSymbols) s = nextIter(it, gSymbols)
proc fileIdx(w: PRodWriter, filename: string): int = proc fileIdx(w: PRodWriter, filename: string): int =
@ -74,175 +72,246 @@ proc newRodWriter(modfilename: string, crc: TCrc32, module: PSym): PRodWriter =
result.tstack = @[] result.tstack = @[]
InitIITable(result.index.tab) InitIITable(result.index.tab)
InitIITable(result.imports.tab) InitIITable(result.imports.tab)
result.index.r = ""
result.imports.r = ""
result.filename = modfilename result.filename = modfilename
result.crc = crc result.crc = crc
result.module = module result.module = module
result.defines = getDefines() result.defines = getDefines()
result.options = options.gOptions result.options = options.gOptions
result.files = @[] result.files = @[]
result.inclDeps = ""
result.modDeps = ""
result.interf = newStringOfCap(2_000)
result.compilerProcs = ""
result.converters = ""
result.init = ""
result.data = newStringOfCap(12_000)
proc addModDep(w: PRodWriter, dep: string) = proc addModDep(w: PRodWriter, dep: string) =
if w.modDeps != nil: app(w.modDeps, " ") if w.modDeps.len != 0: add(w.modDeps, ' ')
app(w.modDeps, encodeInt(fileIdx(w, dep))) encodeVInt(fileIdx(w, dep), w.modDeps)
const const
rodNL = "\x0A" rodNL = "\x0A"
proc addInclDep(w: PRodWriter, dep: string) = proc addInclDep(w: PRodWriter, dep: string) =
app(w.inclDeps, encodeInt(fileIdx(w, dep))) encodeVInt(fileIdx(w, dep), w.inclDeps)
app(w.inclDeps, " ") add(w.inclDeps, " ")
app(w.inclDeps, encodeInt(crcFromFile(dep))) encodeVInt(crcFromFile(dep), w.inclDeps)
app(w.inclDeps, rodNL) add(w.inclDeps, rodNL)
proc pushType(w: PRodWriter, t: PType) = proc pushType(w: PRodWriter, t: PType) =
# check so that the stack does not grow too large: # check so that the stack does not grow too large:
if IiTableGet(w.index.tab, t.id) == invalidKey: if IiTableGet(w.index.tab, t.id) == invalidKey:
var L = len(w.tstack) w.tstack.add(t)
setlen(w.tstack, L + 1)
w.tstack[L] = t
proc pushSym(w: PRodWriter, s: PSym) = proc pushSym(w: PRodWriter, s: PSym) =
# check so that the stack does not grow too large: # check so that the stack does not grow too large:
if IiTableGet(w.index.tab, s.id) == invalidKey: if IiTableGet(w.index.tab, s.id) == invalidKey:
var L = len(w.sstack) w.sstack.add(s)
setlen(w.sstack, L + 1)
w.sstack[L] = s
proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode): PRope = proc encodeNode(w: PRodWriter, fInfo: TLineInfo, n: PNode,
result: var string) =
if n == nil: if n == nil:
# nil nodes have to be stored too: # nil nodes have to be stored too:
return toRope("()") result.add("()")
result = toRope("(") return
app(result, encodeInt(ord(n.kind))) result.add('(')
encodeVInt(ord(n.kind), result)
# we do not write comments for now # we do not write comments for now
# Line information takes easily 20% or more of the filesize! Therefore we # Line information takes easily 20% or more of the filesize! Therefore we
# omit line information if it is the same as the father's line information: # omit line information if it is the same as the father's line information:
if finfo.fileIndex != n.info.fileIndex: if finfo.fileIndex != n.info.fileIndex:
appf(result, "?$1,$2,$3", [encodeInt(n.info.col), encodeInt(n.info.line), result.add('?')
encodeInt(fileIdx(w, toFilename(n.info)))]) encodeVInt(n.info.col, result)
result.add(',')
encodeVInt(n.info.line, result)
result.add(',')
encodeVInt(fileIdx(w, toFilename(n.info)), result)
elif finfo.line != n.info.line: elif finfo.line != n.info.line:
appf(result, "?$1,$2", [encodeInt(n.info.col), encodeInt(n.info.line)]) result.add('?')
encodeVInt(n.info.col, result)
result.add(',')
encodeVInt(n.info.line, result)
elif finfo.col != n.info.col: elif finfo.col != n.info.col:
appf(result, "?$1", [encodeInt(n.info.col)]) result.add('?')
encodeVInt(n.info.col, result)
# No need to output the file index, as this is the serialization of one # No need to output the file index, as this is the serialization of one
# file. # file.
var f = n.flags * PersistentNodeFlags var f = n.flags * PersistentNodeFlags
if f != {}: appf(result, "$$$1", [encodeInt(cast[int32](f))]) if f != {}:
result.add('$')
encodeVInt(cast[int32](f), result)
if n.typ != nil: if n.typ != nil:
appf(result, "^$1", [encodeInt(n.typ.id)]) result.add('^')
encodeVInt(n.typ.id, result)
pushType(w, n.typ) pushType(w, n.typ)
case n.kind case n.kind
of nkCharLit..nkInt64Lit: of nkCharLit..nkInt64Lit:
if n.intVal != 0: appf(result, "!$1", [encodeInt(n.intVal)]) if n.intVal != 0:
result.add('!')
encodeVBiggestInt(n.intVal, result)
of nkFloatLit..nkFloat64Lit: of nkFloatLit..nkFloat64Lit:
if n.floatVal != 0.0: appf(result, "!$1", [encodeStr(w, $n.floatVal)]) if n.floatVal != 0.0:
result.add('!')
encodeStr($n.floatVal, result)
of nkStrLit..nkTripleStrLit: of nkStrLit..nkTripleStrLit:
if n.strVal != "": appf(result, "!$1", [encodeStr(w, n.strVal)]) if n.strVal != "":
result.add('!')
encodeStr(n.strVal, result)
of nkIdent: of nkIdent:
appf(result, "!$1", [encodeStr(w, n.ident.s)]) result.add('!')
encodeStr(n.ident.s, result)
of nkSym: of nkSym:
appf(result, "!$1", [encodeInt(n.sym.id)]) result.add('!')
encodeVInt(n.sym.id, result)
pushSym(w, n.sym) pushSym(w, n.sym)
else: else:
for i in countup(0, sonsLen(n) - 1): for i in countup(0, sonsLen(n) - 1):
app(result, encodeNode(w, n.info, n.sons[i])) encodeNode(w, n.info, n.sons[i], result)
app(result, ")") add(result, ')')
proc encodeLoc(w: PRodWriter, loc: TLoc): PRope = proc encodeLoc(w: PRodWriter, loc: TLoc, result: var string) =
result = nil var oldLen = result.len
if loc.k != low(loc.k): app(result, encodeInt(ord(loc.k))) result.add('<')
if loc.s != low(loc.s): appf(result, "*$1", [encodeInt(ord(loc.s))]) if loc.k != low(loc.k): encodeVInt(ord(loc.k), result)
if loc.flags != {}: appf(result, "$$$1", [encodeInt(cast[int32](loc.flags))]) if loc.s != low(loc.s):
add(result, '*')
encodeVInt(ord(loc.s), result)
if loc.flags != {}:
add(result, '$')
encodeVInt(cast[int32](loc.flags), result)
if loc.t != nil: if loc.t != nil:
appf(result, "^$1", [encodeInt(loc.t.id)]) add(result, '^')
encodeVInt(cast[int32](loc.t.id), result)
pushType(w, loc.t) pushType(w, loc.t)
if loc.r != nil: appf(result, "!$1", [encodeStr(w, ropeToStr(loc.r))]) if loc.r != nil:
if loc.a != 0: appf(result, "?$1", [encodeInt(loc.a)]) add(result, '!')
if result != nil: result = ropef("<$1>", [result]) encodeStr(ropeToStr(loc.r), result)
if loc.a != 0:
add(result, '?')
encodeVInt(loc.a, result)
if oldlen + 1 == result.len:
# no data was necessary, so remove the '<' again:
setLen(result, oldLen)
else:
add(result, '>')
proc encodeType(w: PRodWriter, t: PType): PRope = proc encodeType(w: PRodWriter, t: PType, result: var string) =
if t == nil: if t == nil:
# nil nodes have to be stored too: # nil nodes have to be stored too:
return toRope("[]") result.add("[]")
result = nil return
# we need no surrounding [] here because the type is in a line of its own
if t.kind == tyForward: InternalError("encodeType: tyForward") if t.kind == tyForward: InternalError("encodeType: tyForward")
app(result, encodeInt(ord(t.kind))) encodeVInt(ord(t.kind), result)
appf(result, "+$1", [encodeInt(t.id)]) add(result, '+')
if t.n != nil: app(result, encodeNode(w, UnknownLineInfo(), t.n)) encodeVInt(t.id, result)
if t.flags != {}: appf(result, "$$$1", [encodeInt(cast[int32](t.flags))]) if t.n != nil:
encodeNode(w, UnknownLineInfo(), t.n, result)
if t.flags != {}:
add(result, '$')
encodeVInt(cast[int32](t.flags), result)
if t.callConv != low(t.callConv): if t.callConv != low(t.callConv):
appf(result, "?$1", [encodeInt(ord(t.callConv))]) add(result, '?')
encodeVInt(ord(t.callConv), result)
if t.owner != nil: if t.owner != nil:
appf(result, "*$1", [encodeInt(t.owner.id)]) add(result, '*')
encodeVInt(t.owner.id, result)
pushSym(w, t.owner) pushSym(w, t.owner)
if t.sym != nil: if t.sym != nil:
appf(result, "&$1", [encodeInt(t.sym.id)]) add(result, '&')
encodeVInt(t.sym.id, result)
pushSym(w, t.sym) pushSym(w, t.sym)
if t.size != - 1: appf(result, "/$1", [encodeInt(t.size)]) if t.size != - 1:
if t.align != 2: appf(result, "=$1", [encodeInt(t.align)]) add(result, '/')
if t.containerID != 0: appf(result, "@$1", [encodeInt(t.containerID)]) encodeVBiggestInt(t.size, result)
app(result, encodeLoc(w, t.loc)) if t.align != 2:
add(result, '=')
encodeVInt(t.align, result)
if t.containerID != 0:
add(result, '@')
encodeVInt(t.containerID, result)
encodeLoc(w, t.loc, result)
for i in countup(0, sonsLen(t) - 1): for i in countup(0, sonsLen(t) - 1):
if t.sons[i] == nil: if t.sons[i] == nil:
app(result, "^()") add(result, "^()")
else: else:
appf(result, "^$1", [encodeInt(t.sons[i].id)]) add(result, '^')
encodeVInt(t.sons[i].id, result)
pushType(w, t.sons[i]) pushType(w, t.sons[i])
proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo): PRope = proc encodeLib(w: PRodWriter, lib: PLib, info: TLineInfo, result: var string) =
result = nil add(result, '|')
appf(result, "|$1", [encodeInt(ord(lib.kind))]) encodeVInt(ord(lib.kind), result)
appf(result, "|$1", [encodeStr(w, ropeToStr(lib.name))]) add(result, '|')
appf(result, "|$1", [encodeNode(w, info, lib.path)]) encodeStr(ropeToStr(lib.name), result)
add(result, '|')
encodeNode(w, info, lib.path, result)
proc encodeSym(w: PRodWriter, s: PSym): PRope = proc encodeSym(w: PRodWriter, s: PSym, result: var string) =
var
codeAst: PNode
col, line: PRope
codeAst = nil
if s == nil: if s == nil:
# nil nodes have to be stored too: # nil nodes have to be stored too:
return toRope("{}") result.add("{}")
result = nil return
app(result, encodeInt(ord(s.kind))) # we need no surrounding {} here because the symbol is in a line of its own
appf(result, "+$1", [encodeInt(s.id)]) encodeVInt(ord(s.kind), result)
appf(result, "&$1", [encodeStr(w, s.name.s)]) result.add('+')
encodeVInt(s.id, result)
result.add('&')
encodeStr(s.name.s, result)
if s.typ != nil: if s.typ != nil:
appf(result, "^$1", [encodeInt(s.typ.id)]) result.add('^')
encodeVInt(s.typ.id, result)
pushType(w, s.typ) pushType(w, s.typ)
if s.info.col == int16(- 1): col = nil result.add('?')
else: col = encodeInt(s.info.col) if s.info.col != -1'i16: encodeVInt(s.info.col, result)
if s.info.line == int16(- 1): line = nil result.add(',')
else: line = encodeInt(s.info.line) if s.info.line != -1'i16: encodeVInt(s.info.line, result)
appf(result, "?$1,$2,$3", result.add(',')
[col, line, encodeInt(fileIdx(w, toFilename(s.info)))]) encodeVInt(fileIdx(w, toFilename(s.info)), result)
if s.owner != nil: if s.owner != nil:
appf(result, "*$1", [encodeInt(s.owner.id)]) result.add('*')
encodeVInt(s.owner.id, result)
pushSym(w, s.owner) pushSym(w, s.owner)
if s.flags != {}: appf(result, "$$$1", [encodeInt(cast[int32](s.flags))]) if s.flags != {}:
if s.magic != mNone: appf(result, "@$1", [encodeInt(ord(s.magic))]) result.add('$')
if (s.ast != nil): encodeVInt(cast[int32](s.flags), result)
if s.magic != mNone:
result.add('@')
encodeVInt(ord(s.magic), result)
if s.ast != nil:
var codeAst: PNode = nil
if not astNeeded(s): if not astNeeded(s):
codeAst = s.ast.sons[codePos] codeAst = s.ast.sons[codePos]
# ugly hack to not store the AST:
s.ast.sons[codePos] = nil s.ast.sons[codePos] = nil
app(result, encodeNode(w, s.info, s.ast)) encodeNode(w, s.info, s.ast, result)
if codeAst != nil: if codeAst != nil:
# resore the AST:
s.ast.sons[codePos] = codeAst s.ast.sons[codePos] = codeAst
if s.options != w.options: if s.options != w.options:
appf(result, "!$1", [encodeInt(cast[int32](s.options))]) result.add('!')
if s.position != 0: appf(result, "%$1", [encodeInt(s.position)]) encodeVInt(cast[int32](s.options), result)
if s.offset != - 1: appf(result, "`$1", [encodeInt(s.offset)]) if s.position != 0:
app(result, encodeLoc(w, s.loc)) result.add('%')
if s.annex != nil: app(result, encodeLib(w, s.annex, s.info)) encodeVInt(s.position, result)
if s.offset != - 1:
result.add('`')
encodeVInt(s.offset, result)
encodeLoc(w, s.loc, result)
if s.annex != nil: encodeLib(w, s.annex, s.info, result)
proc addToIndex(w: var TIndex, key, val: int) = proc addToIndex(w: var TIndex, key, val: int) =
if key - w.lastIdxKey == 1: if key - w.lastIdxKey == 1:
# we do not store a key-diff of 1 to safe space # we do not store a key-diff of 1 to safe space
app(w.r, encodeInt(val - w.lastIdxVal)) encodeVInt(val - w.lastIdxVal, w.r)
app(w.r, rodNL)
else: else:
appf(w.r, "$1 $2" & rodNL, encodeVInt(key - w.lastIdxKey, w.r)
[encodeInt(key - w.lastIdxKey), encodeInt(val - w.lastIdxVal)]) add(w.r, ' ')
encodeVInt(val - w.lastIdxVal, w.r)
add(w.r, rodNL)
w.lastIdxKey = key w.lastIdxKey = key
w.lastIdxVal = val w.lastIdxVal = val
IiTablePut(w.tab, key, val) IiTablePut(w.tab, key, val)
@ -250,29 +319,32 @@ proc addToIndex(w: var TIndex, key, val: int) =
var debugWritten: TIntSet var debugWritten: TIntSet
proc symStack(w: PRodWriter) = proc symStack(w: PRodWriter) =
var var i = 0
i, L: int
s, m: PSym
i = 0
while i < len(w.sstack): while i < len(w.sstack):
s = w.sstack[i] var s = w.sstack[i]
if IiTableGet(w.index.tab, s.id) == invalidKey: if IiTableGet(w.index.tab, s.id) == invalidKey:
m = getModule(s) var m = getModule(s)
if m == nil: InternalError("symStack: module nil: " & s.name.s) if m == nil: InternalError("symStack: module nil: " & s.name.s)
if (m.id == w.module.id) or (sfFromGeneric in s.flags): if (m.id == w.module.id) or (sfFromGeneric in s.flags):
# put definition in here # put definition in here
L = ropeLen(w.data) var L = w.data.len
addToIndex(w.index, s.id, L) #intSetIncl(debugWritten, s.id); addToIndex(w.index, s.id, L)
app(w.data, encodeSym(w, s)) #intSetIncl(debugWritten, s.id)
app(w.data, rodNL) encodeSym(w, s, w.data)
add(w.data, rodNL)
if sfExported in s.flags: if sfExported in s.flags:
appf(w.interf, "$1 $2" & rodNL, [encode(s.name.s), encodeInt(s.id)]) encodeStr(s.name.s, w.interf)
add(w.interf, ' ')
encodeVInt(s.id, w.interf)
add(w.interf, rodNL)
if sfCompilerProc in s.flags: if sfCompilerProc in s.flags:
appf(w.compilerProcs, "$1 $2" & rodNL, encodeStr(s.name.s, w.compilerProcs)
[encode(s.name.s), encodeInt(s.id)]) add(w.compilerProcs, ' ')
encodeVInt(s.id, w.compilerProcs)
add(w.compilerProcs, rodNL)
if s.kind == skConverter: if s.kind == skConverter:
if w.converters != nil: app(w.converters, " ") if w.converters.len != 0: add(w.converters, ' ')
app(w.converters, encodeInt(s.id)) encodeVInt(s.id, w.converters)
elif IiTableGet(w.imports.tab, s.id) == invalidKey: elif IiTableGet(w.imports.tab, s.id) == invalidKey:
addToIndex(w.imports, s.id, m.id) #if not Contains(debugWritten, s.id): addToIndex(w.imports, s.id, m.id) #if not Contains(debugWritten, s.id):
# MessageOut(w.filename); # MessageOut(w.filename);
@ -287,10 +359,10 @@ proc typeStack(w: PRodWriter) =
var i = 0 var i = 0
while i < len(w.tstack): while i < len(w.tstack):
if IiTableGet(w.index.tab, w.tstack[i].id) == invalidKey: if IiTableGet(w.index.tab, w.tstack[i].id) == invalidKey:
var L = ropeLen(w.data) var L = w.data.len
addToIndex(w.index, w.tstack[i].id, L) addToIndex(w.index, w.tstack[i].id, L)
app(w.data, encodeType(w, w.tstack[i])) encodeType(w, w.tstack[i], w.data)
app(w.data, rodNL) add(w.data, rodNL)
inc(i) inc(i)
setlen(w.tstack, 0) setlen(w.tstack, 0)
@ -309,66 +381,88 @@ proc addInterfaceSym(w: PRodWriter, s: PSym) =
rawAddInterfaceSym(w, s) rawAddInterfaceSym(w, s)
proc addStmt(w: PRodWriter, n: PNode) = proc addStmt(w: PRodWriter, n: PNode) =
app(w.init, encodeInt(ropeLen(w.data))) encodeVInt(w.data.len, w.init)
app(w.init, rodNL) add(w.init, rodNL)
app(w.data, encodeNode(w, UnknownLineInfo(), n)) encodeNode(w, UnknownLineInfo(), n, w.data)
app(w.data, rodNL) add(w.data, rodNL)
processStacks(w) processStacks(w)
proc writeRod(w: PRodWriter) = proc writeRod(w: PRodWriter) =
processStacks(w) # write header: processStacks(w)
var content = toRope("NIM:") var f: TFile
app(content, toRope(FileVersion)) if not open(f, completeGeneratedFilePath(changeFileExt(w.filename, "rod")),
app(content, rodNL) fmWrite):
app(content, toRope("ID:")) return
app(content, encodeInt(w.module.id)) # write header:
app(content, rodNL) f.write("NIM:")
app(content, toRope("CRC:")) f.write(FileVersion)
app(content, encodeInt(w.crc)) f.write(rodNL)
app(content, rodNL) var id = "ID:"
app(content, toRope("OPTIONS:")) encodeVInt(w.module.id, id)
app(content, encodeInt(cast[int32](w.options))) f.write(id)
app(content, rodNL) f.write(rodNL)
app(content, toRope("DEFINES:"))
app(content, w.defines) var crc = "CRC:"
app(content, rodNL) encodeVInt(w.crc, crc)
app(content, toRope("FILES(" & rodNL)) f.write(crc)
f.write(rodNL)
var options = "OPTIONS:"
encodeVInt(cast[int32](w.options), options)
f.write(options)
f.write(rodNL)
f.write("DEFINES:")
f.write(w.defines)
f.write(rodNL)
var files = "FILES(" & rodNL
for i in countup(0, high(w.files)): for i in countup(0, high(w.files)):
app(content, encode(w.files[i])) encodeStr(w.files[i], files)
app(content, rodNL) files.add(rodNL)
app(content, toRope(')' & rodNL)) f.write(files)
app(content, toRope("INCLUDES(" & rodNL)) f.write(')' & rodNL)
app(content, w.inclDeps)
app(content, toRope(')' & rodNL)) f.write("INCLUDES(" & rodNL)
app(content, toRope("DEPS:")) f.write(w.inclDeps)
app(content, w.modDeps) f.write(')' & rodNL)
app(content, rodNL)
app(content, toRope("INTERF(" & rodNL)) f.write("DEPS:")
app(content, w.interf) f.write(w.modDeps)
app(content, toRope(')' & rodNL)) f.write(rodNL)
app(content, toRope("COMPILERPROCS(" & rodNL))
app(content, w.compilerProcs) f.write("INTERF(" & rodNL)
app(content, toRope(')' & rodNL)) f.write(w.interf)
app(content, toRope("INDEX(" & rodNL)) f.write(')' & rodNL)
app(content, w.index.r)
app(content, toRope(')' & rodNL)) f.write("COMPILERPROCS(" & rodNL)
app(content, toRope("IMPORTS(" & rodNL)) f.write(w.compilerProcs)
app(content, w.imports.r) f.write(')' & rodNL)
app(content, toRope(')' & rodNL))
app(content, toRope("CONVERTERS:")) f.write("INDEX(" & rodNL)
app(content, w.converters) f.write(w.index.r)
app(content, toRope(rodNL)) f.write(')' & rodNL)
app(content, toRope("INIT(" & rodNL))
app(content, w.init) f.write("IMPORTS(" & rodNL)
app(content, toRope(')' & rodNL)) f.write(w.imports.r)
app(content, toRope("DATA(" & rodNL)) f.write(')' & rodNL)
app(content, w.data)
app(content, toRope(')' & rodNL)) f.write("CONVERTERS:")
#MessageOut('interf ' + ToString(ropeLen(w.interf))); f.write(w.converters)
#MessageOut('index ' + ToString(ropeLen(w.indexRope))); f.write(rodNL)
#MessageOut('init ' + ToString(ropeLen(w.init)));
#MessageOut('data ' + ToString(ropeLen(w.data))); f.write("INIT(" & rodNL)
writeRope(content, completeGeneratedFilePath(changeFileExt(w.filename, "rod"))) f.write(w.init)
f.write(')' & rodNL)
f.write("DATA(" & rodNL)
f.write(w.data)
f.write(')' & rodNL)
f.close()
#MessageOut('interf ' + ToString(ropeLen(w.interf)))
#MessageOut('index ' + ToString(ropeLen(w.indexRope)))
#MessageOut('init ' + ToString(ropeLen(w.init)))
#MessageOut('data ' + ToString(ropeLen(w.data)))
proc process(c: PPassContext, n: PNode): PNode = proc process(c: PPassContext, n: PNode): PNode =
result = n result = n
@ -438,6 +532,7 @@ proc myClose(c: PPassContext, n: PNode): PNode =
var w = PRodWriter(c) var w = PRodWriter(c)
writeRod(w) writeRod(w)
result = n result = n
idgen.saveMaxIds(options.projectPath / options.projectName)
proc rodwritePass(): TPass = proc rodwritePass(): TPass =
initPass(result) initPass(result)
@ -446,4 +541,4 @@ proc rodwritePass(): TPass =
result.close = myClose result.close = myClose
result.process = process result.process = process
debugWritten= initIntSet() debugWritten = initIntSet()

View file

@ -14,7 +14,7 @@ import
wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math, wordrecg, ropes, msgs, os, condsyms, idents, renderer, types, platform, math,
magicsys, parser, nversion, semdata, nimsets, semfold, importer, magicsys, parser, nversion, semdata, nimsets, semfold, importer,
procfind, lookups, rodread, pragmas, passes, semtypinst, sigmatch, suggest, procfind, lookups, rodread, pragmas, passes, semtypinst, sigmatch, suggest,
semthreads, intsets, transf, evals semthreads, intsets, transf, evals, idgen
proc semPass*(): TPass proc semPass*(): TPass
# implementation # implementation

View file

@ -158,7 +158,7 @@ proc SemReturn(c: PContext, n: PNode): PNode =
globalError(n.info, errXNotAllowedHere, "\'return\'") globalError(n.info, errXNotAllowedHere, "\'return\'")
if n.sons[0].kind != nkEmpty: if n.sons[0].kind != nkEmpty:
# transform ``return expr`` to ``result = expr; return`` # transform ``return expr`` to ``result = expr; return``
if c.p.resultSym == nil: InternalError(n.info, "semReturn") if c.p.resultSym == nil: globalError(n.info, errNoReturnTypeDeclared)
var a = newNodeI(nkAsgn, n.sons[0].info) var a = newNodeI(nkAsgn, n.sons[0].info)
addSon(a, newSymNode(c.p.resultSym)) addSon(a, newSymNode(c.p.resultSym))
addSon(a, n.sons[0]) addSon(a, n.sons[0])

View file

@ -7,7 +7,7 @@
# distribution, for details about the copyright. # distribution, for details about the copyright.
# #
# Implements a table from trees to trees. Does structural equavilent checking. # Implements a table from trees to trees. Does structural equivalence checking.
import import
hashes, ast, astalgo, types hashes, ast, astalgo, types

View file

@ -118,8 +118,7 @@ proc GetValue*(db: TDbConn, query: TSqlQuery,
## of the first row. Returns "" if the dataset contains no rows. ## of the first row. Returns "" if the dataset contains no rows.
var stmt = setupQuery(db, query, args) var stmt = setupQuery(db, query, args)
if step(stmt) == SQLITE_ROW: if step(stmt) == SQLITE_ROW:
result = newString(column_bytes(stmt, 0)) result = newStringOfCap(column_bytes(stmt, 0))
setLen(result, 0)
add(result, column_text(stmt, 0)) add(result, column_text(stmt, 0))
if finalize(stmt) != SQLITE_OK: dbError(db) if finalize(stmt) != SQLITE_OK: dbError(db)
else: else:

View file

@ -9,6 +9,7 @@ Version 0.8.14
- optional indentation for 'case' statement - optional indentation for 'case' statement
- document & test splicing; don't forget to test negative indexes - document & test splicing; don't forget to test negative indexes
- thread local vs. global raiseHook() - thread local vs. global raiseHook()
- incremental compilation (!)
version 0.9.0 version 0.9.0
@ -105,41 +106,6 @@ Low priority
- timeout for locks - timeout for locks
Super operators
===============
macro (pattern) [T](arg: array[T]) =
Patterns
--------
Patterns are PEGs over the Nimrod AST. Patterns consist of:
type
TPatternKind = enum
pkAny, ## any node (.)
pkNodeClass, ## set of node kinds
pkTerminal, ## 'xyz'
pkNonTerminal, ## a
pkSequence, ## a b c
pkOrderedChoice, ## a / b / c
pkGreedyRep, ## a*
## a+ --> (a a*)
pkOption, ## a?
pkAndPredicate, ## &a
pkNotPredicate, ## !a
pkCapture, ## {a}
pkBackRef, ## $i
pkSearch, ## @a
pkCapturedSearch, ## {@} a
pkRule, ## a <- b
pkGrammar, ## list of rules
Choice operator requires capture.
Version 2 Version 2
========= =========