Files
Nim/nim/syntaxes.pas
2010-02-26 01:26:16 +01:00

235 lines
5.9 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 syntaxes;
// Implements the dispatcher for the different parsers.
{$include 'config.inc'}
interface
uses
nsystem, strutils, llstream, ast, astalgo, idents, scanner, options, msgs,
pnimsyn, pbraces, ptmplsyn, filters, rnimsyn;
type
TFilterKind = (filtNone, filtTemplate, filtReplace, filtStrip);
TParserKind = (skinStandard, skinBraces, skinEndX);
const
parserNames: array [TParserKind] of string = ('standard', 'braces', 'endx');
filterNames: array [TFilterKind] of string = ('none', 'stdtmpl', 'replace',
'strip');
type
TParsers = record
skin: TParserKind;
parser: TParser;
end;
{@ignore}
function ParseFile(const filename: string): PNode;
{@emit
function ParseFile(const filename: string): PNode; procvar;
}
procedure openParsers(var p: TParsers; const filename: string;
inputstream: PLLStream);
procedure closeParsers(var p: TParsers);
function parseAll(var p: TParsers): PNode;
function parseTopLevelStmt(var p: TParsers): PNode;
// implements an iterator. Returns the next top-level statement or nil if end
// of stream.
implementation
function ParseFile(const filename: string): PNode;
var
p: TParsers;
f: TBinaryFile;
begin
if not OpenFile(f, filename) then begin
rawMessage(errCannotOpenFile, filename);
exit
end;
OpenParsers(p, filename, LLStreamOpen(f));
result := ParseAll(p);
CloseParsers(p);
end;
function parseAll(var p: TParsers): PNode;
begin
case p.skin of
skinStandard: result := pnimsyn.parseAll(p.parser);
skinBraces: result := pbraces.parseAll(p.parser);
skinEndX: InternalError('parser to implement');
// skinEndX: result := pendx.parseAll(p.parser);
end
end;
function parseTopLevelStmt(var p: TParsers): PNode;
begin
case p.skin of
skinStandard: result := pnimsyn.parseTopLevelStmt(p.parser);
skinBraces: result := pbraces.parseTopLevelStmt(p.parser);
skinEndX: InternalError('parser to implement');
//skinEndX: result := pendx.parseTopLevelStmt(p.parser);
end
end;
function UTF8_BOM(const s: string): int;
begin
if (s[strStart] = #239) and (s[strStart+1] = #187)
and (s[strStart+2] = #191) then result := 3
else result := 0
end;
function containsShebang(const s: string; i: int): bool;
var
j: int;
begin
result := false;
if (s[i] = '#') and (s[i+1] = '!') then begin
j := i+2;
while s[j] in WhiteSpace do inc(j);
result := s[j] = '/'
end
end;
function parsePipe(const filename: string; inputStream: PLLStream): PNode;
var
line: string;
s: PLLStream;
i: int;
q: TParser;
begin
result := nil;
s := LLStreamOpen(filename, fmRead);
if s <> nil then begin
line := LLStreamReadLine(s) {@ignore} + #0 {@emit};
i := UTF8_Bom(line) + strStart;
if containsShebang(line, i) then begin
line := LLStreamReadLine(s) {@ignore} + #0 {@emit};
i := strStart;
end;
if (line[i] = '#') and (line[i+1] = '!') then begin
inc(i, 2);
while line[i] in WhiteSpace do inc(i);
OpenParser(q, filename, LLStreamOpen(ncopy(line, i)));
result := pnimsyn.parseAll(q);
CloseParser(q);
end;
LLStreamClose(s);
end
end;
function getFilter(ident: PIdent): TFilterKind;
var
i: TFilterKind;
begin
for i := low(TFilterKind) to high(TFilterKind) do
if IdentEq(ident, filterNames[i]) then begin
result := i; exit
end;
result := filtNone
end;
function getParser(ident: PIdent): TParserKind;
var
i: TParserKind;
begin
for i := low(TParserKind) to high(TParserKind) do
if IdentEq(ident, parserNames[i]) then begin
result := i; exit
end;
rawMessage(errInvalidDirectiveX, ident.s);
end;
function getCallee(n: PNode): PIdent;
begin
if (n.kind = nkCall) and (n.sons[0].kind = nkIdent) then
result := n.sons[0].ident
else if n.kind = nkIdent then result := n.ident
else rawMessage(errXNotAllowedHere, renderTree(n));
end;
function applyFilter(var p: TParsers; n: PNode; const filename: string;
input: PLLStream): PLLStream;
var
ident: PIdent;
f: TFilterKind;
begin
ident := getCallee(n);
f := getFilter(ident);
case f of
filtNone: begin
p.skin := getParser(ident);
result := input
end;
filtTemplate: result := filterTmpl(input, filename, n);
filtStrip: result := filterStrip(input, filename, n);
filtReplace: result := filterReplace(input, filename, n);
end;
if f <> filtNone then begin
if gVerbosity >= 2 then begin
rawMessage(hintCodeBegin);
messageOut(result.s);
rawMessage(hintCodeEnd);
end
end
end;
function evalPipe(var p: TParsers; n: PNode; const filename: string;
start: PLLStream): PLLStream;
var
i: int;
begin
result := start;
if n = nil then exit;
if (n.kind = nkInfix) and (n.sons[0].kind = nkIdent)
and IdentEq(n.sons[0].ident, '|'+'') then begin
for i := 1 to 2 do begin
if n.sons[i].kind = nkInfix then
result := evalPipe(p, n.sons[i], filename, result)
else
result := applyFilter(p, n.sons[i], filename, result)
end
end
else if n.kind = nkStmtList then
result := evalPipe(p, n.sons[0], filename, result)
else
result := applyFilter(p, n, filename, result)
end;
procedure openParsers(var p: TParsers; const filename: string;
inputstream: PLLStream);
var
pipe: PNode;
s: PLLStream;
begin
p.skin := skinStandard;
pipe := parsePipe(filename, inputStream);
if pipe <> nil then
s := evalPipe(p, pipe, filename, inputStream)
else
s := inputStream;
case p.skin of
skinStandard, skinBraces, skinEndX:
pnimsyn.openParser(p.parser, filename, s);
end
end;
procedure closeParsers(var p: TParsers);
begin
pnimsyn.closeParser(p.parser);
end;
end.