Files
Nim/nim/main.pas
2010-02-14 00:29:35 +01:00

424 lines
11 KiB
ObjectPascal

//
//
// The Nimrod Compiler
// (c) Copyright 2009 Andreas Rumpf
//
// See the file "copying.txt", included in this
// distribution, for details about the copyright.
//
unit main;
// implements the command dispatcher and several commands as well as the
// module handling
{$include 'config.inc'}
interface
uses
nsystem, llstream, strutils, ast, astalgo, scanner, syntaxes, rnimsyn,
options, msgs, nos, lists, condsyms, paslex, pasparse, rodread, rodwrite,
ropes, trees, wordrecg, sem, semdata, idents, passes, docgen,
extccomp, cgen, ecmasgen, platform, interact, nimconf, importer,
passaux, depends, transf, evals, types;
procedure MainCommand(const cmd, filename: string);
implementation
// ------------------ module handling -----------------------------------------
type
TFileModuleRec = record
filename: string;
module: PSym;
end;
TFileModuleMap = array of TFileModuleRec;
var
compMods: TFileModuleMap = {@ignore} nil {@emit @[]};
// all compiled modules
procedure registerModule(const filename: string; module: PSym);
var
len: int;
begin
len := length(compMods);
setLength(compMods, len+1);
compMods[len].filename := filename;
compMods[len].module := module;
end;
function getModule(const filename: string): PSym;
var
i: int;
begin
for i := 0 to high(compMods) do
if sameFile(compMods[i].filename, filename) then begin
result := compMods[i].module; exit end;
result := nil;
end;
// ----------------------------------------------------------------------------
function newModule(const filename: string): PSym;
begin
// We cannot call ``newSym`` here, because we have to circumvent the ID
// mechanism, which we do in order to assign each module a persistent ID.
new(result);
{@ignore}
fillChar(result^, sizeof(result^), 0);
{@emit}
result.id := -1; // for better error checking
result.kind := skModule;
result.name := getIdent(splitFile(filename).name);
result.owner := result; // a module belongs to itself
result.info := newLineInfo(filename, 1, 1);
include(result.flags, sfUsed);
initStrTable(result.tab);
RegisterModule(filename, result);
StrTableAdd(result.tab, result); // a module knows itself
end;
function CompileModule(const filename: string;
isMainFile, isSystemFile: bool): PSym; forward;
function importModule(const filename: string): PSym;
// this is called by the semantic checking phase
begin
result := getModule(filename);
if result = nil then begin
// compile the module
result := compileModule(filename, false, false);
end
else if sfSystemModule in result.flags then
liMessage(result.info, errAttemptToRedefine, result.Name.s);
end;
function CompileModule(const filename: string;
isMainFile, isSystemFile: bool): PSym;
var
rd: PRodReader;
f: string;
begin
rd := nil;
f := addFileExt(filename, nimExt);
result := newModule(filename);
if isMainFile then include(result.flags, sfMainModule);
if isSystemFile then include(result.flags, sfSystemModule);
if (gCmd = cmdCompileToC) or (gCmd = cmdCompileToCpp) then begin
rd := handleSymbolFile(result, f);
if result.id < 0 then
InternalError('handleSymbolFile should have set the module''s ID');
end
else
result.id := getID();
processModule(result, f, nil, rd);
end;
procedure CompileProject(const filename: string);
begin
{@discard} CompileModule(
JoinPath(options.libpath, addFileExt('system', nimExt)), false, true);
{@discard} CompileModule(addFileExt(filename, nimExt), true, false);
end;
procedure semanticPasses;
begin
registerPass(verbosePass());
registerPass(sem.semPass());
registerPass(transf.transfPass());
end;
procedure CommandGenDepend(const filename: string);
begin
semanticPasses();
registerPass(genDependPass());
registerPass(cleanupPass());
compileProject(filename);
generateDot(filename);
execExternalProgram('dot -Tpng -o' +{&} changeFileExt(filename, 'png') +{&}
' ' +{&} changeFileExt(filename, 'dot'));
end;
procedure CommandCheck(const filename: string);
begin
semanticPasses();
// use an empty backend for semantic checking only
compileProject(filename);
end;
procedure CommandCompileToC(const filename: string);
begin
semanticPasses();
registerPass(cgen.cgenPass());
registerPass(rodwrite.rodwritePass());
//registerPass(cleanupPass());
compileProject(filename);
//for i := low(TTypeKind) to high(TTypeKind) do
// MessageOut('kind: ' +{&} typeKindToStr[i] +{&} ' = ' +{&} toString(sameTypeA[i]));
extccomp.CallCCompiler(changeFileExt(filename, ''));
end;
procedure CommandCompileToEcmaScript(const filename: string);
begin
include(gGlobalOptions, optSafeCode);
setTarget(osEcmaScript, cpuEcmaScript);
initDefines();
semanticPasses();
registerPass(ecmasgenPass());
compileProject(filename);
end;
procedure CommandInteractive();
var
m: PSym;
begin
include(gGlobalOptions, optSafeCode);
setTarget(osNimrodVM, cpuNimrodVM);
initDefines();
registerPass(verbosePass());
registerPass(sem.semPass());
registerPass(transf.transfPass());
registerPass(evals.evalPass());
// load system module:
{@discard} CompileModule(
JoinPath(options.libpath, addFileExt('system', nimExt)), false, true);
m := newModule('stdin');
m.id := getID();
include(m.flags, sfMainModule);
processModule(m, 'stdin', LLStreamOpenStdIn(), nil);
end;
// --------------------------------------------------------------------------
procedure exSymbols(n: PNode);
var
i: int;
begin
case n.kind of
nkEmpty..nkNilLit: begin end; // atoms
nkProcDef..nkIteratorDef: begin
exSymbol(n.sons[namePos]);
end;
nkWhenStmt, nkStmtList: begin
for i := 0 to sonsLen(n)-1 do exSymbols(n.sons[i])
end;
nkVarSection, nkConstSection: begin
for i := 0 to sonsLen(n)-1 do
exSymbol(n.sons[i].sons[0]);
end;
nkTypeSection: begin
for i := 0 to sonsLen(n)-1 do begin
exSymbol(n.sons[i].sons[0]);
if (n.sons[i].sons[2] <> nil) and
(n.sons[i].sons[2].kind = nkObjectTy) then
fixRecordDef(n.sons[i].sons[2])
end
end;
else begin end
end
end;
procedure CommandExportSymbols(const filename: string);
// now unused!
var
module: PNode;
begin
module := parseFile(addFileExt(filename, NimExt));
if module <> nil then begin
exSymbols(module);
renderModule(module, getOutFile(filename, 'pretty.'+NimExt));
end
end;
procedure CommandPretty(const filename: string);
var
module: PNode;
begin
module := parseFile(addFileExt(filename, NimExt));
if module <> nil then
renderModule(module, getOutFile(filename, 'pretty.'+NimExt));
end;
procedure CommandLexPas(const filename: string);
var
L: TPasLex;
tok: TPasTok;
f: string;
stream: PLLStream;
begin
{@ignore}
fillChar(tok, sizeof(tok), 0);
fillChar(L, sizeof(L), 0);
{@emit}
f := addFileExt(filename, 'pas');
stream := LLStreamOpen(f, fmRead);
if stream <> nil then begin
OpenLexer(L, f, stream);
getPasTok(L, tok);
while tok.xkind <> pxEof do begin
printPasTok(tok);
getPasTok(L, tok);
end
end
else
rawMessage(errCannotOpenFile, f);
closeLexer(L);
end;
procedure CommandPas(const filename: string);
var
p: TPasParser;
module: PNode;
f: string;
stream: PLLStream;
begin
f := addFileExt(filename, 'pas');
stream := LLStreamOpen(f, fmRead);
if stream <> nil then begin
OpenPasParser(p, f, stream);
module := parseUnit(p);
closePasParser(p);
renderModule(module, getOutFile(filename, NimExt));
end
else
rawMessage(errCannotOpenFile, f);
end;
procedure CommandScan(const filename: string);
var
L: TLexer;
tok: PToken;
f: string;
stream: PLLStream;
begin
new(tok);
{@ignore}
fillChar(tok^, sizeof(tok^), 0);
{@emit}
f := addFileExt(filename, nimExt);
stream := LLStreamOpen(f, fmRead);
if stream <> nil then begin
openLexer(L, f, stream);
repeat
rawGetTok(L, tok^);
PrintTok(tok);
until tok.tokType = tkEof;
CloseLexer(L);
end
else
rawMessage(errCannotOpenFile, f);
end;
procedure WantFile(const filename: string);
begin
if filename = '' then
liMessage(newLineInfo('command line', 1, 1), errCommandExpectsFilename);
end;
procedure MainCommand(const cmd, filename: string);
begin
appendStr(searchPaths, options.libpath);
if filename <> '' then begin
// current path is always looked first for modules
prependStr(searchPaths, splitFile(filename).dir);
end;
setID(100);
passes.gIncludeFile := syntaxes.parseFile;
passes.gImportModule := importModule;
case whichKeyword(cmd) of
wCompile, wCompileToC, wC, wCC: begin
// compile means compileToC currently
gCmd := cmdCompileToC;
wantFile(filename);
CommandCompileToC(filename);
end;
wCompileToCpp: begin
gCmd := cmdCompileToCpp;
wantFile(filename);
CommandCompileToC(filename);
end;
wCompileToEcmaScript: begin
gCmd := cmdCompileToEcmaScript;
wantFile(filename);
CommandCompileToEcmaScript(filename);
end;
wCompileToLLVM: begin
gCmd := cmdCompileToLLVM;
wantFile(filename);
CommandCompileToC(filename);
end;
wPretty: begin
gCmd := cmdPretty;
wantFile(filename);
//CommandExportSymbols(filename);
CommandPretty(filename);
end;
wDoc: begin
gCmd := cmdDoc;
LoadSpecialConfig(DocConfig);
wantFile(filename);
CommandDoc(filename);
end;
wRst2html: begin
gCmd := cmdRst2html;
LoadSpecialConfig(DocConfig);
wantFile(filename);
CommandRst2Html(filename);
end;
wRst2tex: begin
gCmd := cmdRst2tex;
LoadSpecialConfig(DocTexConfig);
wantFile(filename);
CommandRst2TeX(filename);
end;
wPas: begin
gCmd := cmdPas;
wantFile(filename);
CommandPas(filename);
end;
wBoot: begin
gCmd := cmdBoot;
wantFile(filename);
CommandPas(filename);
end;
wGenDepend: begin
gCmd := cmdGenDepend;
wantFile(filename);
CommandGenDepend(filename);
end;
wListDef: begin
gCmd := cmdListDef;
condsyms.ListSymbols();
end;
wCheck: begin
gCmd := cmdCheck;
wantFile(filename);
CommandCheck(filename);
end;
wParse: begin
gCmd := cmdParse;
wantFile(filename);
{@discard} parseFile(addFileExt(filename, nimExt));
end;
wScan: begin
gCmd := cmdScan;
wantFile(filename);
CommandScan(filename);
MessageOut('Beware: Indentation tokens depend on the parser''s state!');
end;
wI: begin
gCmd := cmdInteractive;
CommandInteractive();
end;
else rawMessage(errInvalidCommandX, cmd);
end
end;
end.