mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-31 18:32:11 +00:00
424 lines
11 KiB
ObjectPascal
Executable File
424 lines
11 KiB
ObjectPascal
Executable File
//
|
|
//
|
|
// 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.
|