diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2008-12-12 14:02:27 +0100 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2008-12-12 14:02:27 +0100 |
commit | ddaedab835fa7ea3457f21a772d636921defdc46 (patch) | |
tree | 8f96b5a3a6700704e0a64bdcdedee1d2caf68517 /nim/rodwrite.pas | |
parent | 2cd136cf7a0210e3cfde7a6f8ba32c9f09560047 (diff) | |
download | Nim-ddaedab835fa7ea3457f21a772d636921defdc46.tar.gz |
version 0.7.2
Diffstat (limited to 'nim/rodwrite.pas')
-rw-r--r-- | nim/rodwrite.pas | 609 |
1 files changed, 609 insertions, 0 deletions
diff --git a/nim/rodwrite.pas b/nim/rodwrite.pas new file mode 100644 index 000000000..64f3d6733 --- /dev/null +++ b/nim/rodwrite.pas @@ -0,0 +1,609 @@ +// +// +// The Nimrod Compiler +// (c) Copyright 2008 Andreas Rumpf +// +// See the file "copying.txt", included in this +// distribution, for details about the copyright. +// +unit rodwrite; + +// This module is responsible for writing of rod files. Note that writing of +// rod files is a pass, reading of rod files is not! This is why reading and +// writing of rod files is split into two different modules. + +interface + +{$include 'config.inc'} + +uses + sysutils, nsystem, nos, options, strutils, nversion, ast, astalgo, msgs, + platform, condsyms, ropes, idents, crc, rodread, passes, importer; + +function rodwritePass(): TPass; + +implementation + +type + TRodWriter = object(TPassContext) + module: PSym; + crc: TCrc32; + options: TOptions; + defines: PRope; + inclDeps: PRope; + modDeps: PRope; + interf: PRope; + compilerProcs: PRope; + index, imports: TIndex; + converters: PRope; + init: PRope; + data: PRope; + filename: string; + sstack: TSymSeq; // a stack of symbols to process + tstack: TTypeSeq; // a stack of types to process + files: TStringSeq; + end; + PRodWriter = ^TRodWriter; + +function newRodWriter(const modfilename: string; crc: TCrc32; + module: PSym): PRodWriter; forward; +procedure addModDep(w: PRodWriter; const dep: string); forward; +procedure addInclDep(w: PRodWriter; const dep: string); forward; +procedure addInterfaceSym(w: PRodWriter; s: PSym); forward; +procedure addStmt(w: PRodWriter; n: PNode); forward; +procedure writeRod(w: PRodWriter); forward; + +function encodeStr(w: PRodWriter; const s: string): PRope; +begin + result := encode(s) +end; + +procedure processStacks(w: PRodWriter); forward; + +function getDefines: PRope; +var + it: TTabIter; + s: PSym; +begin + s := InitTabIter(it, gSymbols); + result := nil; + while s <> nil do begin + if s.position = 1 then begin + if result <> nil then app(result, ' '+''); + app(result, s.name.s); + end; + s := nextIter(it, gSymbols); + end +end; + +function fileIdx(w: PRodWriter; const filename: string): int; +var + i: int; +begin + for i := 0 to high(w.files) do begin + if w.files[i] = filename then begin result := i; exit end; + end; + result := length(w.files); + setLength(w.files, result+1); + w.files[result] := filename; +end; + +function newRodWriter(const modfilename: string; crc: TCrc32; + module: PSym): PRodWriter; +begin + new(result); +{@ignore} + fillChar(result^, sizeof(result^), 0); +{@emit + result.sstack := @[];} +{@emit + result.tstack := @[];} + InitIITable(result.index.tab); + InitIITable(result.imports.tab); + result.filename := modfilename; + result.crc := crc; + result.module := module; + result.defines := getDefines(); + result.options := options.gOptions; + {@emit result.files := @[];} +end; + +procedure addModDep(w: PRodWriter; const dep: string); +begin + if w.modDeps <> nil then app(w.modDeps, ' '+''); + app(w.modDeps, encodeInt(fileIdx(w, dep))); +end; + +const + rodNL = #10+''; + +procedure addInclDep(w: PRodWriter; const dep: string); +begin + app(w.inclDeps, encodeInt(fileIdx(w, dep))); + app(w.inclDeps, ' '+''); + app(w.inclDeps, encodeInt(crcFromFile(dep))); + app(w.inclDeps, rodNL); +end; + +procedure pushType(w: PRodWriter; t: PType); +var + L: int; +begin + // check so that the stack does not grow too large: + if IiTableGet(w.index.tab, t.id) = invalidKey then begin + L := length(w.tstack); + setLength(w.tstack, L+1); + w.tstack[L] := t; + end +end; + +procedure pushSym(w: PRodWriter; s: PSym); +var + L: int; +begin + // check so that the stack does not grow too large: + if IiTableGet(w.index.tab, s.id) = invalidKey then begin + L := length(w.sstack); + setLength(w.sstack, L+1); + w.sstack[L] := s; + end +end; + +function encodeNode(w: PRodWriter; const fInfo: TLineInfo; n: PNode): PRope; +var + i: int; + f: TNodeFlags; +begin + if n = nil then begin + // nil nodes have to be stored too: + result := toRope('()'); exit + end; + result := toRope('('+''); + app(result, encodeInt(ord(n.kind))); + // we do not write comments for now + // 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: + if (finfo.fileIndex <> n.info.fileIndex) then + appf(result, '?$1,$2,$3', [encodeInt(n.info.col), encodeInt(n.info.line), + encodeInt(fileIdx(w, toFilename(n.info)))]) + else if (finfo.line <> n.info.line) then + appf(result, '?$1,$2', [encodeInt(n.info.col), encodeInt(n.info.line)]) + else if (finfo.col <> n.info.col) then + appf(result, '?$1', [encodeInt(n.info.col)]); + // No need to output the file index, as this is the serialization of one + // file. + f := n.flags * PersistentNodeFlags; + if f <> {@set}[] then + appf(result, '$$$1', [encodeInt({@cast}int32(f))]); + if n.typ <> nil then begin + appf(result, '^$1', [encodeInt(n.typ.id)]); + pushType(w, n.typ); + end; + case n.kind of + nkCharLit..nkInt64Lit: begin + if n.intVal <> 0 then + appf(result, '!$1', [encodeInt(n.intVal)]); + end; + nkFloatLit..nkFloat64Lit: begin + if n.floatVal <> 0.0 then + appf(result, '!$1', [encodeStr(w, toStringF(n.floatVal))]); + end; + nkStrLit..nkTripleStrLit: begin + if n.strVal <> '' then + appf(result, '!$1', [encodeStr(w, n.strVal)]); + end; + nkIdent: + appf(result, '!$1', [encodeStr(w, n.ident.s)]); + nkSym: begin + appf(result, '!$1', [encodeInt(n.sym.id)]); + pushSym(w, n.sym); + end; + else begin + for i := 0 to sonsLen(n)-1 do + app(result, encodeNode(w, n.info, n.sons[i])); + end + end; + app(result, ')'+''); +end; + +function encodeLoc(w: PRodWriter; const loc: TLoc): PRope; +begin + result := nil; + if loc.k <> low(loc.k) then + app(result, encodeInt(ord(loc.k))); + if loc.s <> low(loc.s) then + appf(result, '*$1', [encodeInt(ord(loc.s))]); + if loc.flags <> {@set}[] then + appf(result, '$$$1', [encodeInt({@cast}int32(loc.flags))]); + if loc.t <> nil then begin + appf(result, '^$1', [encodeInt(loc.t.id)]); + pushType(w, loc.t); + end; + if loc.r <> nil then + appf(result, '!$1', [encodeStr(w, ropeToStr(loc.r))]); + if loc.a <> 0 then + appf(result, '?$1', [encodeInt(loc.a)]); + if result <> nil then + result := ropef('<$1>', [result]); +end; + +function encodeType(w: PRodWriter; t: PType): PRope; +var + i: int; +begin + if t = nil then begin + // nil nodes have to be stored too: + result := toRope('[]'); exit + end; + result := nil; + if t.kind = tyForward then InternalError('encodeType: tyForward'); + app(result, encodeInt(ord(t.kind))); + appf(result, '+$1', [encodeInt(t.id)]); + if t.n <> nil then + app(result, encodeNode(w, UnknownLineInfo(), t.n)); + if t.flags <> {@set}[] then + appf(result, '$$$1', [encodeInt({@cast}int32(t.flags))]); + if t.callConv <> low(t.callConv) then + appf(result, '?$1', [encodeInt(ord(t.callConv))]); + if t.owner <> nil then begin + appf(result, '*$1', [encodeInt(t.owner.id)]); + pushSym(w, t.owner); + end; + if t.sym <> nil then begin + appf(result, '&$1', [encodeInt(t.sym.id)]); + pushSym(w, t.sym); + end; + if t.size <> -1 then appf(result, '/$1', [encodeInt(t.size)]); + if t.align <> 2 then appf(result, '=$1', [encodeInt(t.align)]); + if t.containerID <> 0 then + appf(result, '@$1', [encodeInt(t.containerID)]); + app(result, encodeLoc(w, t.loc)); + for i := 0 to sonsLen(t)-1 do begin + if t.sons[i] = nil then + app(result, '^()') + else begin + appf(result, '^$1', [encodeInt(t.sons[i].id)]); + pushType(w, t.sons[i]); + end + end; +end; + +function encodeLib(w: PRodWriter; lib: PLib): PRope; +begin + result := nil; + appf(result, '|$1', [encodeInt(ord(lib.kind))]); + appf(result, '|$1', [encodeStr(w, ropeToStr(lib.name))]); + appf(result, '|$1', [encodeStr(w, lib.path)]); +end; + +function encodeSym(w: PRodWriter; s: PSym): PRope; +var + codeAst: PNode; + col, line: PRope; +begin + codeAst := nil; + if s = nil then begin + // nil nodes have to be stored too: + result := toRope('{}'); exit + end; + result := nil; + app(result, encodeInt(ord(s.kind))); + appf(result, '+$1', [encodeInt(s.id)]); + appf(result, '&$1', [encodeStr(w, s.name.s)]); + if s.typ <> nil then begin + appf(result, '^$1', [encodeInt(s.typ.id)]); + pushType(w, s.typ); + end; + if s.info.col = int16(-1) then col := nil + else col := encodeInt(s.info.col); + if s.info.line = int16(-1) then line := nil + else line := encodeInt(s.info.line); + appf(result, '?$1,$2,$3', [col, line, + encodeInt(fileIdx(w, toFilename(s.info)))]); + if s.owner <> nil then begin + appf(result, '*$1', [encodeInt(s.owner.id)]); + pushSym(w, s.owner); + end; + if s.flags <> {@set}[] then + appf(result, '$$$1', [encodeInt({@cast}int32(s.flags))]); + if s.magic <> mNone then + appf(result, '@$1', [encodeInt(ord(s.magic))]); + if (s.ast <> nil) then begin + if not astNeeded(s) then begin + codeAst := s.ast.sons[codePos]; + s.ast.sons[codePos] := nil; + end; + app(result, encodeNode(w, s.info, s.ast)); + if codeAst <> nil then // restore code ast + s.ast.sons[codePos] := codeAst; + end; + if s.options <> w.options then + appf(result, '!$1', [encodeInt({@cast}int32(s.options))]); + if s.position <> 0 then + appf(result, '%$1', [encodeInt(s.position)]); + if s.offset <> -1 then + appf(result, '`$1', [encodeInt(s.offset)]); + app(result, encodeLoc(w, s.loc)); + if s.annex <> nil then + app(result, encodeLib(w, s.annex)); +end; + +procedure addToIndex(var w: TIndex; key, val: int); +begin + if key - w.lastIdxKey = 1 then begin + // we do not store a key-diff of 1 to safe space + app(w.r, encodeInt(val - w.lastIdxVal)); + app(w.r, rodNL); + end + else + appf(w.r, '$1 $2'+rodNL, [encodeInt(key - w.lastIdxKey), + encodeInt(val - w.lastIdxVal)]); + w.lastIdxKey := key; + w.lastIdxVal := val; + IiTablePut(w.tab, key, val); +end; + +var + debugWritten: TIntSet; + +procedure symStack(w: PRodWriter); +var + i, L: int; + s, m: PSym; +begin + i := 0; + while i < length(w.sstack) do begin + s := w.sstack[i]; + if IiTableGet(w.index.tab, s.id) = invalidKey then begin + m := getModule(s); + if m = nil then InternalError('symStack: module nil: ' + s.name.s); + if (m.id = w.module.id) or (sfFromGeneric in s.flags) then begin + // put definition in here + L := ropeLen(w.data); + addToIndex(w.index, s.id, L); + //intSetIncl(debugWritten, s.id); + app(w.data, encodeSym(w, s)); + app(w.data, rodNL); + if sfInInterface in s.flags then + appf(w.interf, '$1 $2'+rodNL, [encode(s.name.s), encodeInt(s.id)]); + if sfCompilerProc in s.flags then + appf(w.compilerProcs, '$1 $2'+rodNL, [encode(s.name.s), encodeInt(s.id)]); + if s.kind = skConverter then begin + if w.converters <> nil then app(w.converters, ' '+''); + app(w.converters, encodeInt(s.id)) + end + end + else if IiTableGet(w.imports.tab, s.id) = invalidKey then begin + addToIndex(w.imports, s.id, m.id); + //if not IntSetContains(debugWritten, s.id) then begin + // MessageOut(w.filename); + // debug(s.owner); + // debug(s); + // InternalError('BUG!!!!'); + //end + end + end; + inc(i); + end; + setLength(w.sstack, 0); +end; + +procedure typeStack(w: PRodWriter); +var + i, L: int; +begin + i := 0; + while i < length(w.tstack) do begin + if IiTableGet(w.index.tab, w.tstack[i].id) = invalidKey then begin + L := ropeLen(w.data); + addToIndex(w.index, w.tstack[i].id, L); + app(w.data, encodeType(w, w.tstack[i])); + app(w.data, rodNL); + end; + inc(i); + end; + setLength(w.tstack, 0); +end; + +procedure processStacks(w: PRodWriter); +begin + while (length(w.tstack) > 0) or (length(w.sstack) > 0) do begin + symStack(w); + typeStack(w); + end +end; + +procedure rawAddInterfaceSym(w: PRodWriter; s: PSym); +begin + pushSym(w, s); + processStacks(w); +end; + +procedure addInterfaceSym(w: PRodWriter; s: PSym); +begin + if w = nil then exit; + if [sfInInterface, sfCompilerProc] * s.flags <> [] then begin + rawAddInterfaceSym(w, s); + end +end; + +procedure addStmt(w: PRodWriter; n: PNode); +begin + app(w.init, encodeInt(ropeLen(w.data))); + app(w.init, rodNL); + app(w.data, encodeNode(w, UnknownLineInfo(), n)); + app(w.data, rodNL); + processStacks(w); +end; + +procedure writeRod(w: PRodWriter); +var + content: PRope; + i: int; +begin + processStacks(w); + // write header: + content := toRope('NIM:'); + app(content, toRope(FileVersion)); + app(content, rodNL); + app(content, toRope('ID:')); + app(content, encodeInt(w.module.id)); + app(content, rodNL); + app(content, toRope('CRC:')); + app(content, encodeInt(w.crc)); + app(content, rodNL); + app(content, toRope('OPTIONS:')); + app(content, encodeInt({@cast}int32(w.options))); + app(content, rodNL); + app(content, toRope('DEFINES:')); + app(content, w.defines); + app(content, rodNL); + app(content, toRope('FILES('+rodNL)); + for i := 0 to high(w.files) do begin + app(content, encode(w.files[i])); + app(content, rodNL); + end; + app(content, toRope(')'+rodNL)); + app(content, toRope('INCLUDES('+rodNL)); + app(content, w.inclDeps); + app(content, toRope(')'+rodNL)); + app(content, toRope('DEPS:')); + app(content, w.modDeps); + app(content, rodNL); + app(content, toRope('INTERF('+rodNL)); + app(content, w.interf); + app(content, toRope(')'+rodNL)); + app(content, toRope('COMPILERPROCS('+rodNL)); + app(content, w.compilerProcs); + app(content, toRope(')'+rodNL)); + app(content, toRope('INDEX('+rodNL)); + app(content, w.index.r); + app(content, toRope(')'+rodNL)); + app(content, toRope('IMPORTS('+rodNL)); + app(content, w.imports.r); + app(content, toRope(')'+rodNL)); + app(content, toRope('CONVERTERS:')); + app(content, w.converters); + app(content, toRope(rodNL)); + app(content, toRope('INIT('+rodNL)); + app(content, w.init); + app(content, toRope(')'+rodNL)); + app(content, toRope('DATA('+rodNL)); + app(content, w.data); + app(content, toRope(')'+rodNL)); + + //MessageOut('interf ' + ToString(ropeLen(w.interf))); + //MessageOut('index ' + ToString(ropeLen(w.indexRope))); + //MessageOut('init ' + ToString(ropeLen(w.init))); + //MessageOut('data ' + ToString(ropeLen(w.data))); + + writeRope(content, + completeGeneratedFilePath(changeFileExt(w.filename, 'rod'))); +end; + +function process(c: PPassContext; n: PNode): PNode; +var + i, j: int; + w: PRodWriter; + a: PNode; + s: PSym; +begin + result := n; + if c = nil then exit; + w := PRodWriter(c); + case n.kind of + nkStmtList: begin + for i := 0 to sonsLen(n)-1 do {@discard} process(c, n.sons[i]); + end; + nkTemplateDef, nkMacroDef: begin + s := n.sons[namePos].sym; + addInterfaceSym(w, s); + end; + nkProcDef, nkIteratorDef, nkConverterDef: begin + s := n.sons[namePos].sym; + if s = nil then InternalError(n.info, 'rodwrite.process'); + if (n.sons[codePos] <> nil) or (s.magic <> mNone) + or not (sfForward in s.flags) then begin + addInterfaceSym(w, s); + end + end; + nkVarSection: begin + for i := 0 to sonsLen(n)-1 do begin + a := n.sons[i]; + if a.kind <> nkIdentDefs then InternalError(a.info, 'rodwrite.process'); + addInterfaceSym(w, a.sons[0].sym); + end + end; + nkConstSection: begin + for i := 0 to sonsLen(n)-1 do begin + a := n.sons[i]; + if a.kind <> nkConstDef then InternalError(a.info, 'rodwrite.process'); + addInterfaceSym(w, a.sons[0].sym); + end + end; + nkTypeSection: begin + for i := 0 to sonsLen(n)-1 do begin + a := n.sons[i]; + if a.sons[0].kind <> nkSym then + InternalError(a.info, 'rodwrite.process'); + s := a.sons[0].sym; + addInterfaceSym(w, s); // this takes care of enum fields too + // Note: The check for ``s.typ.kind = tyEnum`` is wrong for enum + // type aliasing! Otherwise the same enum symbol would be included + // several times! + (* + if (a.sons[2] <> nil) and (a.sons[2].kind = nkEnumTy) then begin + a := s.typ.n; + for j := 0 to sonsLen(a)-1 do + addInterfaceSym(w, a.sons[j].sym); + end *) + end + end; + nkImportStmt: begin + for i := 0 to sonsLen(n)-1 do addModDep(w, getModuleFile(n.sons[i])); + addStmt(w, n); + end; + nkFromStmt: begin + addModDep(w, getModuleFile(n.sons[0])); + addStmt(w, n); + end; + nkIncludeStmt: begin + for i := 0 to sonsLen(n)-1 do addInclDep(w, getModuleFile(n.sons[i])); + end; + nkPragma: addStmt(w, n); + else begin end + end; +end; + +function myOpen(module: PSym; const filename: string): PPassContext; +var + w: PRodWriter; +begin + if module.id < 0 then InternalError('rodwrite: module ID not set'); + w := newRodWriter(filename, rodread.GetCRC(filename), module); + rawAddInterfaceSym(w, module); + result := w; +end; + +function myClose(c: PPassContext; n: PNode): PNode; +var + w: PRodWriter; +begin + w := PRodWriter(c); + writeRod(w); + result := n; +end; + +function rodwritePass(): TPass; +begin + initPass(result); + if optSymbolFiles in gGlobalOptions then begin + result.open := myOpen; + result.close := myClose; + result.process := process; + end +end; + +initialization + IntSetInit(debugWritten); +end. |