mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-29 17:34:43 +00:00
235 lines
5.9 KiB
ObjectPascal
235 lines
5.9 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 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.
|