mirror of
https://github.com/sboydlns/univacemulators.git
synced 2026-01-13 23:47:03 +00:00
2536 lines
82 KiB
ObjectPascal
2536 lines
82 KiB
ObjectPascal
unit Compiler;
|
|
|
|
interface
|
|
|
|
uses SysUtils, Classes, Generics.Collections, SrcFile, Tokens, CodeGen, U494CodeGen,
|
|
Statements, Symbols, Expressions, Literals, Contnrs, AnsiStrings;
|
|
|
|
type
|
|
TTargetType = ( tgtUnknown, tgt494 );
|
|
|
|
TCompiler = class
|
|
private
|
|
FSrcFile: TSrcFile;
|
|
FAsmFile: TSrcFile;
|
|
FTokenGen: TTokenGen;
|
|
FBlocks: TBlockStack;
|
|
FLiterals: TLiteralList;
|
|
FTempVars: TSymbolTable;
|
|
FCodeGen: TCodeGen;
|
|
FTokenTrace: Boolean;
|
|
FInFile: String;
|
|
FOutDir: String;
|
|
FAsmFileName: String;
|
|
FErrorCount: Integer;
|
|
FTargetType: TTargetType;
|
|
FSymbolNum: Integer;
|
|
FSwitchNum: Integer;
|
|
FBlockNum: Integer;
|
|
FConditionalEndLblNum: Integer;
|
|
function Assignment(depth: Integer; dt: TDeclType): Boolean;
|
|
function Block(isPgm, isMain, isProc: Boolean): Boolean;
|
|
function Declaration: Boolean;
|
|
function DesignationalExpr: TExpression;
|
|
procedure Endd;
|
|
procedure Error(e: String);
|
|
function Expression: TExpression;
|
|
function FindSymbol(id: AnsiString; st: TSymbolType): TSymbol;
|
|
function Forr: Boolean;
|
|
function Gotoo: Boolean;
|
|
function Iff: Boolean;
|
|
function Labell: Boolean;
|
|
function NewBlock(isPgm, isMain, isProc: Boolean): TBlock;
|
|
function NewSymbol(id: AnsiString; st: TSymbolType; isArray: Boolean): TSymbol;
|
|
function ProcedureDecl: Boolean;
|
|
function Programm: Boolean;
|
|
function Statement: Boolean;
|
|
function Write: Boolean;
|
|
public
|
|
constructor Create;
|
|
function Compile(InFile: String; tokenTrace: Boolean; outDir: String;
|
|
tgt: TTargetType): Integer;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses IOUtils, Math;
|
|
|
|
{ TCompiler }
|
|
|
|
function TCompiler.Assignment(depth: Integer; dt: TDeclType): Boolean;
|
|
var
|
|
ttype1: TTokenType;
|
|
token1: AnsiString;
|
|
sym: TSymbol;
|
|
expr: TExpression;
|
|
asg: TExprVariable;
|
|
|
|
function InAssignment: Boolean;
|
|
// We need to scan ahead here to see what the next operator is. If it is := then we are part
|
|
// part of an assignment. Otherwise we are part of an expression.
|
|
var
|
|
save: TTokenStack;
|
|
ttype: TTokenType;
|
|
token: AnsiString;
|
|
t: TToken;
|
|
begin
|
|
save := TTokenStack.Create;
|
|
try
|
|
ttype := ttUnknown;
|
|
while (ttype <> ttLineSeparator) and (ttype <> ttEof) and (ttype <> ttAssignment) do
|
|
begin
|
|
FTokenGen.Get(ttype, token);
|
|
t.TType := ttype;
|
|
t.Value := token;
|
|
save.Push(t);
|
|
end;
|
|
Result := (ttype = ttAssignment);
|
|
while (save.Count > 0) do
|
|
begin
|
|
t := save.Pop;
|
|
FTokenGen.Unget(t.TType, t.Value);
|
|
end;
|
|
finally
|
|
save.Free;
|
|
end;
|
|
end;
|
|
|
|
procedure Arrayy;
|
|
var
|
|
ttype: TTokenType;
|
|
token: AnsiString;
|
|
begin
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype <> ttLeftParen) then
|
|
raise Exception.CreateFmt('Expected left bracket following array identifier, got %s', [token]);
|
|
|
|
//
|
|
TExprArray(asg).Subscripts := TList<TExpressionTerm>.Create;
|
|
repeat
|
|
expr := Expression;
|
|
if ((not Assigned(expr)) or (expr.TargetDecl <> dtInteger)) then
|
|
Error('Integer expression expected for array subscript');
|
|
TExprArray(asg).Subscripts.Add(expr);
|
|
FTokenGen.Get(ttype1, token1);
|
|
until ttype1 <> ttComma;
|
|
if (ttype1 <> ttRightParen) then
|
|
begin
|
|
Error('Subscript list missing closing bracket');
|
|
FTokenGen.Unget(ttype1, token1);
|
|
end;
|
|
if (TExprArray(asg).Subscripts.Count <> sym.ArraySubscripts) then
|
|
Error(Format('Incorrect number of array subscripts for %s', [sym.ID]));
|
|
end;
|
|
|
|
procedure Stringg;
|
|
// Check to see if substring specified and return the start and end values of
|
|
// the substring.
|
|
var
|
|
ttype: TTokenType;
|
|
token: AnsiString;
|
|
elit: TExprLiteral;
|
|
lit: TLiteral;
|
|
begin
|
|
// Check for left paren. If not present, we are done.
|
|
FTokenGen.Get(ttype, token);
|
|
if (sym.IsArray and (ttype <> ttLeftParen)) then
|
|
raise Exception.CreateFmt('Expected left bracket following array identifier, got %s', [token]);
|
|
if (ttype <> ttLeftParen) then
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
Exit;
|
|
end;
|
|
// This gets complicated. Values given inside [] may be either
|
|
// substring start / length or array subscripts or both.
|
|
//
|
|
// First gather all values until we see something that is not a
|
|
// comma. If variable is not an array then values are substring
|
|
// start / length and we are done.
|
|
//
|
|
// If variable is an array and next token is not a colon then
|
|
// values are array subscripts and we are done.
|
|
//
|
|
// If next value is a token, copy assumed array subscripts to
|
|
// substring start / length and continue parsing to find array
|
|
// subscripts.
|
|
if (asg) is TExprArray then
|
|
TExprArray(asg).Subscripts := TList<TExpressionTerm>.Create;
|
|
repeat
|
|
expr := Expression;
|
|
if ((not Assigned(expr)) or (expr.TargetDecl <> dtInteger)) then
|
|
Error('Integer expression expected for substring / array subscript');
|
|
if (sym.IsArray) then
|
|
TExprArray(asg).Subscripts.Add(expr)
|
|
else if (not Assigned(TExprVariable(asg).StringStart)) then
|
|
TExprVariable(asg).StringStart := expr
|
|
else if (not Assigned(TExprVariable(asg).StringLength)) then
|
|
TExprVariable(asg).StringLength := expr
|
|
else
|
|
begin
|
|
Error('Too many values given for substring');
|
|
end;
|
|
FTokenGen.Get(ttype, token);
|
|
until ttype <> ttComma;
|
|
//
|
|
if (ttype = ttColon) then
|
|
begin
|
|
if (sym.IsArray) then
|
|
begin
|
|
if (TExprArray(asg).Subscripts.Count > 2) then
|
|
begin
|
|
Error('Too many values given for substring');
|
|
end else
|
|
begin
|
|
with TExprArray(asg) do
|
|
begin
|
|
StringStart := Subscripts[0];
|
|
Subscripts.Delete(0);
|
|
if (Subscripts.Count > 0) then
|
|
begin
|
|
StringLength := Subscripts[1];
|
|
Subscripts.Delete(0);
|
|
end;
|
|
end;
|
|
end;
|
|
repeat
|
|
expr := Expression;
|
|
if ((not Assigned(expr)) or (expr.TargetDecl <> dtInteger)) then
|
|
begin
|
|
Error('Integer expression expected for array subscript');
|
|
end;
|
|
TExprArray(asg).Subscripts.Add(expr);
|
|
FTokenGen.Get(ttype, token);
|
|
until ttype <> ttComma;
|
|
end else
|
|
begin
|
|
Error('Colon only allowed for string arrays');
|
|
end;
|
|
end;
|
|
//
|
|
if (ttype <> ttRightParen) then
|
|
begin
|
|
Error(Format('Right bracket expected, got %s', [token]));
|
|
FTokenGen.Unget(ttype, token);
|
|
end;
|
|
if (sym.IsArray) then
|
|
begin
|
|
if (TExprArray(asg).Subscripts.Count <> sym.ArraySubscripts) then
|
|
Error('Incorrect number of array subscripts');
|
|
end;
|
|
with TExprVariable(asg) do
|
|
begin
|
|
if (Assigned(StringStart) and (not Assigned(StringLength))) then
|
|
begin
|
|
elit := TExprLiteral.Create;
|
|
lit := TLiteral.Create;
|
|
lit.Value := '1';
|
|
lit.DeclType := dtInteger;
|
|
elit.Literal := lit;
|
|
StringLength := elit;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
Result := False;
|
|
FTokenGen.Get(ttype1, token1);
|
|
if (ttype1 <> ttIdentifier) then
|
|
begin
|
|
FTokenGen.Unget(ttype1, token1);
|
|
Exit;
|
|
end;
|
|
if (not InAssignment) then
|
|
begin
|
|
FTokenGen.Unget(ttype1, token1);
|
|
Exit;
|
|
end;
|
|
|
|
Result := True;
|
|
expr := nil;
|
|
asg := nil;
|
|
|
|
sym := FindSymbol(token1, stVariable);
|
|
if (not Assigned(sym)) then
|
|
begin
|
|
Error(Format('%s is undefined', [token1]));
|
|
FTokenGen.FlushLine;
|
|
Exit;
|
|
end;
|
|
|
|
try
|
|
if (Assigned(sym)) then
|
|
begin
|
|
if ((sym.DeclType <> dt) and (dt <> dtUnknown)) then
|
|
Error('All variables in an assignment must be the same type');
|
|
if (sym.IsArray) then
|
|
asg := TExprArray.Create
|
|
else
|
|
asg := TExprVariable.Create;
|
|
asg.Symbol := sym;
|
|
if (sym.DeclType = dtString) then
|
|
begin
|
|
try
|
|
Stringg;
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
Error(E.Message);
|
|
FTokenGen.FlushLine;
|
|
Exit;
|
|
end;
|
|
end;
|
|
end else
|
|
begin
|
|
if (sym.IsArray) then
|
|
begin
|
|
try
|
|
Arrayy;
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
Error(E.Message);
|
|
FTokenGen.FlushLine;
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
// burn to := operator
|
|
FTokenGen.Get(ttype1, token1);
|
|
if (ttype1 <> ttAssignment) then
|
|
raise Exception.CreateFmt('OOPS! Internal error. Expected := got %s', [token1]);
|
|
|
|
if (not Assignment(depth + 1, sym.DeclType)) then
|
|
begin
|
|
expr := Expression;
|
|
if (not Assigned(expr)) then
|
|
begin
|
|
Error('Expected an assignment or an expression following :=');
|
|
FTokenGen.Get(ttype1, token1);
|
|
while ((ttype1 <> ttLineSeparator) and (ttype1 <> ttEof)) do
|
|
FTokenGen.Get(ttype1, token1);
|
|
Exit;
|
|
end else if (expr.ErrorCount <> 0) then
|
|
begin
|
|
Exit;
|
|
end else if (Assigned(sym) and
|
|
((sym.DeclType = dtReal) and (not expr.IsNumeric)) or
|
|
((sym.DeclType = dtInteger) and ( not expr.IsNumeric))) then
|
|
begin
|
|
Error('Target variable type does not match expression type');
|
|
Exit;
|
|
end else if (Assigned(sym) and (sym.DeclType = dtLogical) and (expr.TargetDecl <> dtLogical)) then
|
|
begin
|
|
Error('Not a boolean expression');
|
|
Exit;
|
|
end;
|
|
FCodeGen.Expression(expr);
|
|
end;
|
|
|
|
if (depth = 0) then
|
|
begin
|
|
FTokenGen.Get(ttype1, token1);
|
|
if (ttype1 <> ttLineSeparator) then
|
|
begin
|
|
Error(Format('Expected line separator, got %s', [token1]));
|
|
FTokenGen.Unget(ttype1, token1);
|
|
end;
|
|
end;
|
|
|
|
if (Assigned(asg)) then
|
|
FCodeGen.Assignment(asg, expr, depth);
|
|
finally
|
|
expr.Free;
|
|
asg.Free;
|
|
end;
|
|
end;
|
|
|
|
function TCompiler.Block(isPgm, isMain, isProc: Boolean): Boolean;
|
|
var
|
|
ttype: TTokenType;
|
|
token: AnsiString;
|
|
b: TBlock;
|
|
I: Integer;
|
|
begin
|
|
FTokenGen.Get(ttype, token);
|
|
if ((ttype = ttLabel) or (ttype = ttBegin)) then
|
|
begin
|
|
if (isPgm) then
|
|
FCodeGen.PgmStart(isMain);
|
|
end;
|
|
FTokenGen.Unget(ttype, token);
|
|
while (Labell) do
|
|
;
|
|
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype <> ttBegin) then
|
|
begin
|
|
Result := False;
|
|
FTokenGen.Unget(ttype, token);
|
|
Exit;
|
|
end;
|
|
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype = ttComment) then
|
|
FTokenGen.FlushLine
|
|
else
|
|
FTokenGen.Unget(ttype, token);
|
|
|
|
Result := True;
|
|
b := NewBlock(False, isMain, isProc);
|
|
FBlocks.Push(b);
|
|
try
|
|
FCodeGen.BlockBegin(b);
|
|
|
|
while (Declaration) do
|
|
;
|
|
|
|
FCodeGen.BlockStart(b);
|
|
|
|
while (Statement) do
|
|
;
|
|
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype = ttEnd) then
|
|
begin
|
|
Endd;
|
|
for i := 0 to b.Symbols.Count - 1 do
|
|
begin
|
|
if (b.Symbols.Symbols[i].IsForward) then
|
|
Error(Format('Forward symbol %s not defined', [b.Symbols.Symbols[i].ID]));
|
|
end;
|
|
FCodeGen.BlockEnd(b);
|
|
end else
|
|
begin
|
|
Error(Format('Expected END, got %s', [token]));
|
|
FTokenGen.Unget(ttype, token);
|
|
end;
|
|
finally
|
|
FBlocks.Pop.Free;
|
|
end;
|
|
end;
|
|
|
|
function TCompiler.Compile(InFile: String; tokenTrace: Boolean; outDir: String;
|
|
tgt: TTargetType): Integer;
|
|
var
|
|
ispgm: Boolean;
|
|
begin
|
|
try
|
|
FInFile := InFile;
|
|
FTokenTrace := tokenTrace;
|
|
FOutDir := outDir;
|
|
FTargetType := tgt;
|
|
FSrcFile := TSrcFile.Create(InFile);
|
|
FAsmFileName := TPath.GetDirectoryName(InFile) + '\' +
|
|
TPath.GetFileNameWithoutExtension(InFile) + '.s';
|
|
FAsmFile := TSrcFile.Create(FAsmFileName, fmCreate);
|
|
case FTargetType of
|
|
tgtUnknown: raise Exception.Create('Unknown target type');
|
|
tgt494: FCodeGen := T494CodeGen.Create(FAsmFile);
|
|
end;
|
|
FTokenGen := TTokenGen.Create(FSrcFile, FCodeGen, FTokenTrace);
|
|
|
|
ispgm := Programm;
|
|
if (not ispgm) then
|
|
begin
|
|
while (ProcedureDecl) do // If not a program, do external subroutines
|
|
;
|
|
end;
|
|
|
|
FCodeGen.PgmEnd(ispgm, FLiterals, FTempVars);
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
WriteLn(E.Message);
|
|
Inc(FErrorCount);
|
|
end;
|
|
end;
|
|
|
|
FreeAndNil(FCodeGen);
|
|
FreeAndNil(FSrcFile);
|
|
FreeAndNil(FAsmFile);
|
|
WriteLn(Format('%-20.20s: %d error(s) encountered', [TPath.GetFileName(FInFIle), FErrorCount]));
|
|
Result := FErrorCount;
|
|
end;
|
|
|
|
constructor TCompiler.Create;
|
|
begin
|
|
FBlocks := TBlockStack.Create;
|
|
FLiterals := TLiteralList.Create;
|
|
FTempVars := TSymbolTable.Create;
|
|
end;
|
|
|
|
function TCompiler.Declaration: Boolean;
|
|
var
|
|
ttype: TTokenType;
|
|
token: AnsiString;
|
|
d: TDeclaration;
|
|
sym: TSymbol;
|
|
b: TBlock;
|
|
done: Boolean;
|
|
|
|
procedure ArraySubscripts; forward;
|
|
|
|
function SubString(id: AnsiString; sym: TSymbol): Integer;
|
|
var
|
|
ttype: TTokenType;
|
|
token: AnsiString;
|
|
len: Integer;
|
|
sub: TSubstringSymbol;
|
|
sym1: TSymbol;
|
|
begin
|
|
Result := 0;
|
|
if (b.Symbols.TryGetValue(id, stAny, sym1)) then
|
|
begin
|
|
Error(Format('%s is multipy defined', [id]));
|
|
Exit;
|
|
end;
|
|
sub := TSubstringSymbol.Create;
|
|
sub.ID := id;
|
|
sub.SymbolType := stVariable;
|
|
sub.DeclType := dtString;
|
|
sub.IsStatic := sym.IsStatic;
|
|
if (sym is TSubstringSymbol) then
|
|
begin
|
|
sub.Start := TSubstringSymbol(sym).Start +
|
|
TSubstringSymbol(sym).Length;
|
|
sub.Container := TSubstringSymbol(sym).Container;
|
|
sub.SymbolNum := TSubstringSymbol(sym).SymbolNum;
|
|
end else
|
|
begin
|
|
sub.Start := sym.StringLength + 1;
|
|
sub.Container := sym;
|
|
sub.SymbolNum := sym.SymbolNum;
|
|
end;
|
|
b.Symbols.Add(id, sub);
|
|
//
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype <> ttLeftParen) then
|
|
begin
|
|
Error(Format('Left bracket expected for substring, got %s', [token]));
|
|
FTokenGen.Unget(ttype, token);
|
|
end;
|
|
while ((ttype <> ttRightParen) and (ttype <> ttLineSeparator) and (ttype <> ttEof)) do
|
|
begin
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype = ttInteger) then
|
|
begin
|
|
if (not TryStrToInt(String(token), len)) then
|
|
len := 0;
|
|
if ((len < 1) or (len > 32767)) then
|
|
Error('String length must be >= 1 and < 32K');
|
|
Inc(sub.Length, len);
|
|
end else if (ttype = ttIdentifier) then
|
|
begin
|
|
Inc(sub.Length, SubString(token, sub));
|
|
end else if (ttype = ttComma) then
|
|
begin
|
|
;
|
|
end else
|
|
Break;
|
|
end;
|
|
if (ttype <> ttRightParen) then
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
Error(Format('Expected right bracket to end substring, got %s', [token]));
|
|
end {else
|
|
FTokenGen.Get(ttype, token)};
|
|
Result := sub.Length;
|
|
end;
|
|
|
|
procedure SubscriptsToSubstrings(sym: TSymbol);
|
|
var
|
|
i: Integer;
|
|
sym1: TSymbol;
|
|
begin
|
|
for i := 0 to b.Symbols.Count - 1 do
|
|
begin
|
|
sym1 := b.Symbols.Symbols[i];
|
|
if (sym1 is TSubstringSymbol) then
|
|
begin
|
|
if (TSubstringSymbol(sym1).Container = sym) then
|
|
begin
|
|
sym1.IsArray := True;
|
|
sym1.ArraySubscripts := sym.ArraySubscripts;
|
|
sym1.ArraySize := sym.ArraySize;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure Stringg(sym: TSymbol);
|
|
var
|
|
len: Integer;
|
|
begin
|
|
sym.StringLength := 0;
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype <> ttLeftParen) then
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
Error(Format('Left bracket expected following STRING declaration, got %s', [token]));
|
|
Exit;
|
|
end;
|
|
while ((ttype <> ttRightParen) and (ttype <> ttLineSeparator) and (ttype <> ttEof)) do
|
|
begin
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype = ttInteger) then
|
|
begin
|
|
if (not TryStrToInt(String(token), len)) then
|
|
len := 0;
|
|
if ((len < 1) or (len > 32767)) then
|
|
Error('String length must be >= 1 and < 32K');
|
|
Inc(sym.StringLength, len);
|
|
end else if (ttype = ttIdentifier) then
|
|
begin
|
|
Inc(sym.StringLength, SubString(token, sym));
|
|
end else if (ttype = ttComma) then
|
|
begin
|
|
;
|
|
end else if (ttype = ttColon) then
|
|
begin
|
|
sym.IsArray := True;
|
|
ArraySubscripts;
|
|
SubscriptsToSubstrings(sym);
|
|
Inc(MinHeapSize, sym.ArraySize);
|
|
FCodeGen.NewArray(sym);
|
|
end else
|
|
Break;
|
|
end;
|
|
if (ttype <> ttRightParen) then
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
Error(Format('Expected right bracket to end STRING, got %s', [token]));
|
|
end;
|
|
Inc(MinHeapSize, sym.StringLength + 1);
|
|
end;
|
|
|
|
function Variable(isArray: Boolean): TSymbol;
|
|
begin
|
|
Result := nil;
|
|
if (b.Symbols.TryGetValue(token, stAny, sym)) then
|
|
begin
|
|
Error(Format('%s is multipy defined', [token]));
|
|
Exit;
|
|
end;
|
|
sym := NewSymbol(token, stVariable, isArray);
|
|
Result := sym;
|
|
b.Symbols.Add(token, sym);
|
|
sym.DeclType := d.DType;
|
|
if (b.IsProc) then
|
|
sym.IsStatic := False
|
|
else
|
|
sym.IsStatic := True;
|
|
if (sym.DeclType = dtString) then
|
|
Stringg(sym);
|
|
if (sym.IsStatic) then
|
|
FCodeGen.StaticVar(sym)
|
|
else
|
|
FCodeGen.StackVar(sym);
|
|
end;
|
|
|
|
procedure Local;
|
|
var
|
|
st: TSymbolType;
|
|
begin
|
|
FTokenGen.Get(ttype, token);
|
|
if (token = 'LABEL') then
|
|
st := stLabel
|
|
else
|
|
begin
|
|
Error(Format('%s is not valid for LOCAL', [token]));
|
|
FTokenGen.FlushLine;
|
|
Exit;
|
|
end;
|
|
|
|
b := FBlocks.Peek;
|
|
done := False;
|
|
while (not done) do
|
|
begin
|
|
FTokenGen.Get(ttype, token);
|
|
case ttype of
|
|
ttIdentifier:
|
|
begin
|
|
if (b.Symbols.TryGetValue(token, stAny, sym)) then
|
|
begin
|
|
Error(Format('%s is multipy defined', [token]));
|
|
Exit;
|
|
end;
|
|
sym := NewSymbol(token, st, False);
|
|
b.Symbols.Add(token, sym);
|
|
sym.IsForward := True;
|
|
end;
|
|
ttEof:
|
|
begin
|
|
Error('Unexpected end-of-file');
|
|
Exit;
|
|
end;
|
|
ttLineSeparator:
|
|
done := True;
|
|
ttComma:
|
|
Continue;
|
|
else
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
Error(Format('Expected identifier or comma, got %s', [token]));
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure Switch;
|
|
var
|
|
swtch: TSwitchSymbol;
|
|
expr: TExpression;
|
|
begin
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype <> ttIdentifier) then
|
|
begin
|
|
Error(Format('Identifier expected got %s', [token]));
|
|
FTokenGen.FlushLine;
|
|
Exit;
|
|
end;
|
|
b := FBlocks.Peek;
|
|
if (b.Symbols.TryGetValue(token, stSwitch, sym)) then
|
|
begin
|
|
Error(Format('%s is multiply defined', [token]));
|
|
FTokenGen.FlushLine;
|
|
Exit;
|
|
end;
|
|
sym := NewSymbol(token, stSwitch, False);
|
|
b.Symbols.Add(token, sym);
|
|
swtch := (TSwitchSymbol(sym));
|
|
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype <> ttAssignment) then
|
|
begin
|
|
Error(Format('Equal sign expected got %s', [token]));
|
|
FTokenGen.FlushLine;
|
|
Exit;
|
|
end;
|
|
|
|
done := False;
|
|
while (not done) do
|
|
begin
|
|
Inc(FSwitchNum);
|
|
if (swtch.FirstSwitchNum = 0) then
|
|
swtch.FirstSwitchNum := FSwitchNum;
|
|
expr := DesignationalExpr;
|
|
if (Assigned(expr)) then
|
|
swtch.Targets.Add(expr)
|
|
else
|
|
Error('Designational expression expected.');
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype = ttComma) then
|
|
Continue
|
|
else if (ttype = ttLineSeparator) then
|
|
done := True
|
|
else
|
|
begin
|
|
Error(Format('Comma or line separator expected, got %s', [token]));
|
|
FTokenGen.FlushLine;
|
|
end;
|
|
end;
|
|
FCodeGen.Switch(swtch);
|
|
end;
|
|
|
|
procedure ArraySubscripts;
|
|
var
|
|
expr: TExpression;
|
|
i, len, i1, i2: Integer;
|
|
allLiterals: Boolean;
|
|
begin
|
|
allLiterals := True;
|
|
sym.ArraySubscripts := 0;
|
|
Inc(FSymbolNum);
|
|
sym.NewArrayLabelNum := FSymbolNum;
|
|
repeat
|
|
expr := Expression;
|
|
if ((not Assigned(expr)) or (expr.TargetDecl <> dtInteger)) then
|
|
Error('Subscript expressions must be of type integer');
|
|
sym.Indices.Add(expr);
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype = ttColon) then
|
|
begin
|
|
expr := Expression;
|
|
if ((not Assigned(expr)) or (expr.TargetDecl <> dtInteger)) then
|
|
Error('Subscript expressions must be of type integer');
|
|
if (Assigned(expr) and (not (TExpressionTerm(expr) is TExprLiteral))) then
|
|
allLiterals := False;
|
|
sym.Indices.Add(expr);
|
|
end else
|
|
raise Exception.Create('Subscript not of form ll:uu');
|
|
Inc(sym.ArraySubscripts);
|
|
FTokenGen.Get(ttype, token);
|
|
until ttype <> ttComma;
|
|
if (ttype <> ttRightParen) then
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
Error('Subscript list missing closing bracket');
|
|
end;
|
|
if (sym.ArraySubscripts > 10) then
|
|
Error('Too many array subscripts (max. 10)');
|
|
// Calculate total length of static arrays
|
|
sym.ArraySize := 0;
|
|
if (allLiterals) then
|
|
begin
|
|
case sym.DeclType of
|
|
dtInteger,
|
|
dtLogical:
|
|
len := 1;
|
|
dtReal:
|
|
len := 2;
|
|
dtString:
|
|
len := ((sym.StringLength - 1) div 5) + 2;
|
|
else
|
|
len := 1;
|
|
end;
|
|
for i := sym.Indices.Count - 1 downto 0 do
|
|
begin
|
|
if ((i mod 2) = 0) then
|
|
begin
|
|
if (not TryStrToInt(String(TExprLiteral(sym.Indices[i]).Literal.Value), i1)) then
|
|
i1 := 0;
|
|
len := ((i2 - i1) + 1) * len
|
|
end else
|
|
begin
|
|
if (not TryStrToInt(String(TExprLiteral(sym.Indices[i]).Literal.Value), i2)) then
|
|
i2 := 0;
|
|
end;
|
|
end;
|
|
sym.ArraySize := len + sym.Indices.Count + 1;
|
|
end;
|
|
end;
|
|
|
|
procedure SubscriptsToPrior(syms: TList<TSymbol>);
|
|
// Make all preceeding variables reference the same
|
|
// array defintion.
|
|
var
|
|
sym1: TSymbol;
|
|
begin
|
|
for sym1 in syms do
|
|
begin
|
|
if (sym1 <> sym) then
|
|
begin
|
|
sym1.ArraySubscripts := sym.ArraySubscripts;
|
|
sym1.NewArrayLabelNum := sym.NewArrayLabelNum;
|
|
sym1.ArraySize := sym.ArraySize;
|
|
Inc(MinHeapSize, sym.ArraySize);
|
|
end;
|
|
end;
|
|
syms.Clear;
|
|
end;
|
|
|
|
procedure Arrayy;
|
|
var
|
|
syms: TList<TSymbol>;
|
|
begin
|
|
syms := TList<TSymbol>.Create;
|
|
try
|
|
FTokenGen.Get(ttype, token);
|
|
while (ttype = ttIdentifier) do
|
|
begin
|
|
sym := Variable(True);
|
|
syms.Add(sym);
|
|
if ((sym.DeclType = dtString) and (sym.ArraySubscripts <> 0)) then
|
|
SubscriptsToPrior(syms);
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype = ttComma) then
|
|
begin
|
|
FTokenGen.Get(ttype, token);
|
|
Continue;
|
|
end else if (sym.DeclType = dtString) then
|
|
begin
|
|
Continue;
|
|
end else if (ttype <> ttLeftParen) then
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
Error('An array declaration must specify some subscripts');
|
|
Break;
|
|
end;
|
|
try
|
|
ArraySubscripts;
|
|
Inc(MinHeapSize, sym.ArraySize);
|
|
FCodeGen.NewArray(sym);
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
Error(E.Message);
|
|
FTokenGen.FlushLine;
|
|
end;
|
|
end;
|
|
SubscriptsToPrior(syms);
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype = ttComma) then
|
|
FTokenGen.Get(ttype, token);
|
|
end;
|
|
if (ttype <> ttLineSeparator) then
|
|
begin
|
|
Error(Format('Line separator expected, got %s', [token]));
|
|
FTokenGen.Unget(ttype, token);
|
|
end;
|
|
finally
|
|
syms.Free;
|
|
end;
|
|
end;
|
|
|
|
begin
|
|
if (ProcedureDecl) then
|
|
begin
|
|
Result := True;
|
|
Exit;
|
|
end;
|
|
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype = ttComment) then
|
|
begin
|
|
FTokenGen.FlushLine;
|
|
Result := True;
|
|
Exit;
|
|
end else if (token = 'LOCAL') then
|
|
begin
|
|
Result := True;
|
|
Local;
|
|
Exit;
|
|
end else if (token = 'SWITCH') then
|
|
begin
|
|
Result := True;
|
|
Switch;
|
|
Exit;
|
|
end;
|
|
|
|
if (ttype = ttDeclarator) then
|
|
begin
|
|
for d in DeclTypes do
|
|
begin
|
|
if (d.ID = token) then
|
|
Break;
|
|
end;
|
|
if (d.DType = dtArray) then
|
|
begin
|
|
Result := True;
|
|
Error('Variable type required before ARRAY');
|
|
Exit;
|
|
end;
|
|
if (d.DType = dtUnknown) then
|
|
begin
|
|
Result := False;
|
|
FTokenGen.Unget(ttype, token);
|
|
Exit;
|
|
end;
|
|
end else
|
|
begin
|
|
Result := False;
|
|
FTokenGen.Unget(ttype, token);
|
|
Exit;
|
|
end;
|
|
|
|
Result := True;
|
|
b := FBlocks.Peek;
|
|
|
|
FTokenGen.Get(ttype, token);
|
|
if (token = 'ARRAY') then
|
|
begin
|
|
Result := True;
|
|
Arrayy;
|
|
Exit;
|
|
end else
|
|
FTokenGen.Unget(ttype, token);
|
|
|
|
done := False;
|
|
while (not done) do
|
|
begin
|
|
FTokenGen.Get(ttype, token);
|
|
case ttype of
|
|
ttIdentifier:
|
|
Variable(False);
|
|
ttEof:
|
|
begin
|
|
Error('Unexpected end-of-file');
|
|
Exit;
|
|
end;
|
|
ttLineSeparator:
|
|
done := True;
|
|
ttComma:
|
|
Continue;
|
|
else
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
Error(Format('Expected identifier or comma, got %s', [token]));
|
|
Exit;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCompiler.DesignationalExpr: TExpression;
|
|
var
|
|
ttype: TTokenType;
|
|
token: AnsiString;
|
|
sym: TSymbol;
|
|
elbl: TExprLabel;
|
|
eswtch: TExprSwitch;
|
|
iff: TExprIf;
|
|
begin
|
|
Result := nil;
|
|
FTokenGen.Get(ttype, token);
|
|
case ttype of
|
|
ttIdentifier:
|
|
begin
|
|
sym := FindSymbol(token, stLabel);
|
|
if (Assigned(sym)) then
|
|
begin
|
|
elbl := TExprLabel.Create;
|
|
elbl.Symbol := sym;
|
|
Result := TExpression(elbl);
|
|
end else
|
|
begin
|
|
sym := FindSymbol(token, stSwitch);
|
|
if (Assigned(sym)) then
|
|
begin
|
|
eswtch := TExprSwitch.Create;
|
|
eswtch.Symbol := sym;
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype <> ttLeftParen) then
|
|
Error(Format('Expected left bracket, got %s', [token]));
|
|
eswtch.Index := Expression;
|
|
if (eswtch.Index.TargetDecl <> dtInteger) then
|
|
Error(Format('Integer expression expected for SWITCH %s', [eswtch.Symbol.ID]));
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype <> ttRightParen) then
|
|
Error(Format('Expected right bracket, got %s', [token]));
|
|
Result := TExpression(eswtch);
|
|
end else
|
|
begin
|
|
Error(Format('%s is not a LABEL or a SWITCH', [token]));
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
ttIf:
|
|
begin
|
|
iff := TExprIf.Create;
|
|
iff.IfExpr := Expression;
|
|
if (iff.IfExpr.TargetDecl = dtLogical) then
|
|
begin
|
|
FTokenGen.Get(ttype, token);
|
|
if (token <> 'THEN') then
|
|
begin
|
|
Error(Format('THEN expected, got %s', [token]));
|
|
FTokenGen.Unget(ttype, token);
|
|
end;
|
|
iff.ElseLblNum := FSymbolNum + 1;
|
|
iff.EndLblNum := FSymbolNum + 2;
|
|
Inc(FSymbolNum, 2);
|
|
iff.ThenExpr := DesignationalExpr;
|
|
if (not Assigned(iff.ThenExpr)) then
|
|
Error('Designational expression expected following THEN');
|
|
if (Assigned(iff.ThenExpr) and (iff.ThenExpr is TExprIf)) then
|
|
Error('IF not allowed following THEN');
|
|
FTokenGen.Get(ttype, token);
|
|
if (token <> 'ELSE') then
|
|
begin
|
|
Error(Format('ELSE expected, got %s', [token]));
|
|
FTokenGen.Unget(ttype, token);
|
|
end;
|
|
iff.ElseExpr := DesignationalExpr;
|
|
if (not Assigned(iff.ElseExpr)) then
|
|
Error('Designational expression expected following ELSE');
|
|
Result := TExpression(iff);
|
|
end else
|
|
begin
|
|
Error('Relational expression expected following IF');
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TCompiler.Endd;
|
|
var
|
|
ttype: TTokenType;
|
|
token: AnsiString;
|
|
begin
|
|
FTokenGen.Get(ttype, token);
|
|
while ((ttype <> ttEof) and (ttype <> ttEnd) and
|
|
(ttype <> ttLineSeparator) and (token <> 'ELSE')) do
|
|
FTokenGen.Get(ttype, token);
|
|
FTokenGen.Unget(ttype, token);
|
|
end;
|
|
|
|
procedure TCompiler.Error(e: String);
|
|
var
|
|
s: String;
|
|
begin
|
|
s := Format('%d: %s', [FSrcFile.LineNum, e]);
|
|
WriteLn(s);
|
|
FCodeGen.Comment(AnsiString('**** ' + s));
|
|
Inc(FerrorCount);
|
|
end;
|
|
|
|
function TCompiler.Expression: TExpression;
|
|
const
|
|
oprs: array [TExprOperatorType] of AnsiString = (
|
|
'**', '-', '*', '/', '//', '+', '-', 'LSS', 'LEQ',
|
|
'GTR', 'GEQ', 'EQL', 'NEQ', 'NOT', 'AND', 'OR',
|
|
'XOR', 'IMPL', 'EQUIV', 'IF', 'THEN', 'ELSE',
|
|
'GET_ARRAY', 'PUSH', 'SIGN', 'UNK'
|
|
);
|
|
|
|
function Factor(priorityLevel, condDepth: Integer; var errCount: Integer): TExpressionTerm; forward;
|
|
|
|
function OperatorType(opr: AnsiString): TExprOperatorType;
|
|
var
|
|
i: TExprOperatorType;
|
|
begin
|
|
for i := Low(oprs) to High(oprs) do
|
|
begin
|
|
if (opr = oprs[i]) then
|
|
begin
|
|
Result := i;
|
|
Exit;
|
|
end;
|
|
end;
|
|
Result := otUnknown;
|
|
end;
|
|
|
|
function PriorityMatch(opr: AnsiString; priorityLevel: Integer): Boolean;
|
|
var
|
|
s: AnsiString;
|
|
begin
|
|
Result := False;
|
|
for s in ExprOprPriorities[priorityLevel] do
|
|
begin
|
|
if (s = opr) then
|
|
begin
|
|
Result := True;
|
|
Break;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function Arrayy(var errCount: Integer; condDepth: Integer; sym: TSymbol): TExpressionTerm;
|
|
var
|
|
ttype: TTokenType;
|
|
token: AnsiString;
|
|
expr: TExpressionTerm;
|
|
arr: TExprArray;
|
|
opr: TExprOperator;
|
|
// process array items by treating the array bracket as a high priority operator
|
|
begin
|
|
Result := nil;
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype <> ttLeftParen) then
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
Error(Format('Expected left bracket following array identifier, got %s', [token]));
|
|
Inc(errCount);
|
|
Exit;
|
|
end;
|
|
arr := TExprArray.Create;
|
|
arr.Symbol := sym;
|
|
repeat
|
|
expr := Factor(0, condDepth, errCount);
|
|
if ((not Assigned(expr)) or (expr.TargetDecl <> dtInteger)) then
|
|
begin
|
|
Error('Integer expression expected for array subscript');
|
|
Inc(errCount);
|
|
end;
|
|
arr.Subscripts.Add(expr);
|
|
FTokenGen.Get(ttype, token);
|
|
until ttype <> ttComma;
|
|
if (ttype <> ttRightParen) then
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
Error(Format('Expected right bracket, got %s', [token]));
|
|
end;
|
|
if (arr.Subscripts.Count <> sym.ArraySubscripts) then
|
|
begin
|
|
Error('Incorrect number of array subscripts');
|
|
Inc(errCount);
|
|
end;
|
|
opr := TExprOperator.Create;
|
|
opr.OType := otGetArray;
|
|
opr.Right := arr;
|
|
opr.TargetDecl := sym.DeclType;
|
|
Result := opr;
|
|
end;
|
|
|
|
function Stringg(var errCount: Integer; condDepth: Integer; sym: TSymbol): TExpressionTerm;
|
|
// Check to see if substring specified and return the start and end values of
|
|
// the substring.
|
|
var
|
|
ttype: TTokenType;
|
|
token: AnsiString;
|
|
expr: TExpressionTerm;
|
|
opr: TExprOperator;
|
|
elit: TExprLiteral;
|
|
lit: TLiteral;
|
|
begin
|
|
if (sym.IsArray) then
|
|
Result := TExprArray.Create
|
|
else
|
|
Result := TExprVariable.Create;
|
|
TExprVariable(Result).Symbol := sym;
|
|
TExprVariable(Result).TargetDecl := sym.DeclType;
|
|
// Check for left paren. If not present, we are done.
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype <> ttLeftParen) then
|
|
begin
|
|
if (sym.IsArray) then
|
|
begin
|
|
Error(Format('Left bracket expected for array reference, got %s', [token]));
|
|
Inc(errCount);
|
|
end;
|
|
FTokenGen.Unget(ttype, token);
|
|
Exit;
|
|
end;
|
|
// This gets complicated. Values given inside [] may be either
|
|
// substring start / length or array subscripts or both.
|
|
//
|
|
// First gather all values until we see something that is not a
|
|
// comma. If variable is not an array then values are substring
|
|
// start / length and we are done.
|
|
//
|
|
// If variable is an array and next token is not a colon then
|
|
// values are array subscripts and we are done.
|
|
//
|
|
// If next value is a token, copy assumed array subscripts to
|
|
// substring start / length and continue parsing to find array
|
|
// subscripts.
|
|
repeat
|
|
expr := Factor(0, condDepth, errCount);
|
|
if ((not Assigned(expr)) or (expr.TargetDecl <> dtInteger)) then
|
|
begin
|
|
Error('Integer expression expected for substring / array subscript');
|
|
Inc(errCount);
|
|
end;
|
|
if (sym.IsArray) then
|
|
TExprArray(Result).Subscripts.Add(expr)
|
|
else if (not Assigned(TExprVariable(Result).StringStart)) then
|
|
TExprVariable(Result).StringStart := expr
|
|
else if (not Assigned(TExprVariable(Result).StringLength)) then
|
|
TExprVariable(Result).StringLength := expr
|
|
else
|
|
begin
|
|
Error('Too many values given for substring');
|
|
Inc(errCount);
|
|
end;
|
|
FTokenGen.Get(ttype, token);
|
|
until ttype <> ttComma;
|
|
//
|
|
if (ttype = ttColon) then
|
|
begin
|
|
if (sym.IsArray) then
|
|
begin
|
|
if (TExprArray(Result).Subscripts.Count > 2) then
|
|
begin
|
|
Error('Too many values given for substring');
|
|
Inc(errCount);
|
|
end else
|
|
begin
|
|
with TExprArray(Result) do
|
|
begin
|
|
StringStart := Subscripts[0];
|
|
Subscripts.Delete(0);
|
|
if (Subscripts.Count > 0) then
|
|
begin
|
|
StringLength := Subscripts[1];
|
|
Subscripts.Delete(0);
|
|
end;
|
|
end;
|
|
end;
|
|
repeat
|
|
expr := Factor(0, condDepth, errCount);
|
|
if ((not Assigned(expr)) or (expr.TargetDecl <> dtInteger)) then
|
|
begin
|
|
Error('Integer expression expected for array subscript');
|
|
Inc(errCount);
|
|
end;
|
|
TExprArray(Result).Subscripts.Add(expr);
|
|
FTokenGen.Get(ttype, token);
|
|
until ttype <> ttComma;
|
|
end else
|
|
begin
|
|
Error('Colon only allowed for string arrays');
|
|
Inc(errCount);
|
|
end;
|
|
end;
|
|
//
|
|
if (ttype <> ttRightParen) then
|
|
begin
|
|
Error(Format('Right bracket expected, got %s', [token]));
|
|
Inc(errCount);
|
|
FTokenGen.Unget(ttype, token);
|
|
end;
|
|
with TExprVariable(Result) do
|
|
begin
|
|
if (Assigned(StringStart) and (not Assigned(StringLength))) then
|
|
begin
|
|
elit := TExprLiteral.Create;
|
|
lit := TLiteral.Create;
|
|
lit.Value := '1';
|
|
lit.DeclType := dtInteger;
|
|
elit.Literal := lit;
|
|
StringLength := elit;
|
|
end;
|
|
end;
|
|
if (sym.IsArray) then
|
|
begin
|
|
if (TExprArray(Result).Subscripts.Count <> sym.ArraySubscripts) then
|
|
begin
|
|
Error('Incorrect number of array subscripts');
|
|
Inc(errCount);
|
|
end;
|
|
opr := TExprOperator.Create;
|
|
opr.OType := otGetArray;
|
|
opr.Right := Result;
|
|
opr.TargetDecl := sym.DeclType;
|
|
Result := opr;
|
|
end;
|
|
end;
|
|
|
|
function Operand(var errCount: Integer; condDepth: Integer): TExpressionTerm;
|
|
// top of the priority hierarchy. Looking for identifiers, literals
|
|
// and parentheses.
|
|
var
|
|
ttype: TTokenType;
|
|
token: AnsiString;
|
|
i, itemp: Integer;
|
|
ftemp: Double;
|
|
sym: TSymbol;
|
|
evar: TExprVariable;
|
|
elit: TExprLiteral;
|
|
ifOpr: TExprIf;
|
|
begin
|
|
Result := nil;
|
|
try
|
|
FTokenGen.Get(ttype, token);
|
|
case ttype of
|
|
ttIf:
|
|
begin
|
|
ifOpr := TExprIf.Create;
|
|
ifOpr.IfExpr := Factor(0, condDepth, errCount);
|
|
if ((not Assigned(ifOpr.IfExpr)) or (ifOpr.IfExpr.DeclType <> dtLogical)) then
|
|
begin
|
|
Error('Boolean expression expected following IF');
|
|
Inc(errCount);
|
|
end;
|
|
Inc(FSymbolNum);
|
|
ifOpr.ElseLblNum := FSymbolNum;
|
|
// If we are parsing the outermost IF then we need to set the label number
|
|
// of the label marking the end of the entire IF chain.
|
|
if (condDepth = 0) then
|
|
begin
|
|
Inc(FSymbolNum);
|
|
FConditionalEndLblNum := FSymbolNum;
|
|
end;
|
|
ifOpr.EndLblNum := FConditionalEndLblNum;
|
|
ifOpr.ConditionalDepth := condDepth;
|
|
Inc(condDepth);
|
|
FTokenGen.Get(ttype, token);
|
|
if (token <> 'THEN') then
|
|
begin
|
|
Error(Format('THEN expected, got %s', [token]));
|
|
Inc(errCount);
|
|
FTokenGen.Unget(ttype, token);
|
|
end;
|
|
ifOpr.ThenExpr := Factor(0, condDepth, errCount);
|
|
if (not Assigned(ifOpr.ThenExpr)) then
|
|
raise Exception.Create('Expression expected following THEN');
|
|
if (Assigned(ifOpr.ThenExpr) and (ifOpr.ThenExpr is TExprIf)) then
|
|
begin
|
|
Error('IF not allowed following THEN');
|
|
Inc(errCount);
|
|
end;
|
|
FTokenGen.Get(ttype, token);
|
|
if (token <> 'ELSE') then
|
|
begin
|
|
Error(Format('ELSE expected, got %s', [token]));
|
|
Inc(errCount);
|
|
FTokenGen.Unget(ttype, token);
|
|
end;
|
|
IfOpr.ElseExpr := Factor(0, condDepth, errCount);
|
|
if (not Assigned(ifOpr.ElseExpr)) then
|
|
raise Exception.Create('Expression expected following ELSE');
|
|
Result := ifOpr;
|
|
Exit;
|
|
end;
|
|
ttIdentifier:
|
|
begin
|
|
sym := FindSymbol(token, stVariable);
|
|
if (not Assigned(sym)) then
|
|
begin
|
|
Error(Format('%s is undefined', [token]));
|
|
sym := NewSymbol(token, stVariable, False);
|
|
sym.DeclType := dtInteger;
|
|
Inc(errCount);
|
|
end;
|
|
if (sym.DeclType = dtString) then
|
|
Result := Stringg(errCount, condDepth, sym)
|
|
else if (sym.IsArray) then
|
|
Result := Arrayy(errCount, condDepth, sym)
|
|
else
|
|
begin
|
|
evar := TExprVariable.Create;
|
|
evar.Symbol := sym;
|
|
evar.TargetDecl := sym.DeclType;
|
|
Result := evar;
|
|
end;
|
|
Exit;
|
|
end;
|
|
ttInteger:
|
|
begin
|
|
if (not TryStrToInt(String(token), itemp)) then
|
|
begin
|
|
Error(Format('%s is not a valid integer', [token]));
|
|
Inc(errCount);
|
|
end;
|
|
i := FLiterals.Add(dtInteger, token);
|
|
elit := TExprLiteral.Create;
|
|
elit.Literal := FLiterals[i];
|
|
elit.TargetDecl := dtInteger;
|
|
Result := elit;
|
|
Exit;
|
|
end;
|
|
ttReal:
|
|
begin
|
|
if (not TryStrToFloat(String(token), ftemp)) then
|
|
begin
|
|
Error(Format('%s is not a valid real number', [token]));
|
|
Inc(errCount);
|
|
end;
|
|
if (token[1] = '.') then
|
|
token := '0' + token;
|
|
i := FLiterals.Add(dtReal, token);
|
|
elit := TExprLiteral.Create;
|
|
elit.Literal := FLiterals[i];
|
|
elit.TargetDecl := dtReal;
|
|
Result := elit;
|
|
Exit;
|
|
end;
|
|
ttEof:
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
raise Exception.Create('Unexpected end-of-file');
|
|
end;
|
|
ttLineSeparator:
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
raise Exception.Create('Expression syntax error');
|
|
end;
|
|
ttLeftParen:
|
|
begin
|
|
Result := Expression;
|
|
Inc(errCount, Result.ErrorCount);
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype <> ttRightParen) then
|
|
raise Exception.Create('Unbalanced parenthese');
|
|
end;
|
|
ttLogicalValue:
|
|
begin
|
|
if (token = 'TRUE') then
|
|
i := FLiterals.Add(dtLogical, '1')
|
|
else
|
|
i := FLiterals.Add(dtLogical, '0');
|
|
elit := TExprLiteral.Create;
|
|
elit.Literal := FLiterals[i];
|
|
elit.TargetDecl := dtLogical;
|
|
Result := elit;
|
|
Exit;
|
|
end;
|
|
ttString:
|
|
begin
|
|
i := FLiterals.Add(dtString, token);
|
|
elit := TExprLiteral.Create;
|
|
elit.Literal := FLiterals[i];
|
|
elit.TargetDecl := dtString;
|
|
Result := elit;
|
|
Exit;
|
|
end;
|
|
else
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
raise Exception.Create('Expression Syntax error');
|
|
end;
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
Error(E.Message);
|
|
Inc(errCount);
|
|
FreeAndNil(Result);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure CheckOperands(opr: TExprOperator; var errCount: Integer);
|
|
// Validate operand types against the operator type.
|
|
begin
|
|
if ((not Assigned(opr.Left)) and (not Assigned(opr.Right))) then
|
|
Exit;
|
|
|
|
if (Assigned(opr.Left)) then
|
|
begin
|
|
opr.TargetDecl := opr.Left.DeclType;
|
|
opr.Left.TargetDecl := opr.Left.DeclType;
|
|
opr.Right.TargetDecl := opr.Right.DeclType;
|
|
if ((opr.Right.DeclType = dtInteger) and (opr.Left.DeclType = dtReal)) then
|
|
begin
|
|
opr.Right.TargetDecl := dtReal;
|
|
opr.TargetDecl := dtReal;
|
|
end;
|
|
if ((opr.Left.DeclType = dtInteger) and (opr.Right.DeclType = dtReal)) then
|
|
begin
|
|
opr.Left.TargetDecl := dtReal;
|
|
opr.TargetDecl := dtReal;
|
|
end;
|
|
if (opr.IsLogical or opr.IsRelational) then
|
|
opr.TargetDecl := dtLogical;
|
|
if ((opr.OType = otExponent) or (opr.OType = otDivide)) then
|
|
begin
|
|
opr.Left.TargetDecl := dtReal;
|
|
opr.Right.TargetDecl := dtReal;
|
|
opr.TargetDecl := dtReal;
|
|
end else if (opr.OType = otIntDivide) then
|
|
begin
|
|
if ((opr.Left.DeclType <> dtInteger) or (opr.Right.DeclType <> dtInteger)) then
|
|
begin
|
|
Error('Both operands of integer divide must be integer');
|
|
Inc(errCount);
|
|
end;
|
|
end;
|
|
if (opr.IsArithmetic and (not (opr.Left.TargetIsNumeric and opr.Right.TargetIsNumeric))) then
|
|
begin
|
|
Error('Non-numeric operand specified for arithmetic operator');
|
|
Inc(errCount);
|
|
end;
|
|
if (opr.IsRelational and
|
|
(not ((opr.Left.TargetIsNumeric and opr.Right.TargetIsNumeric) or
|
|
(opr.Left.TargetIsString and opr.Right.TargetIsString)))) then
|
|
begin
|
|
Error('Non-numeric or non-string operand(s) specified for relational operator');
|
|
Inc(errCount);
|
|
end;
|
|
if (opr.IsLogical and (not (opr.Left.TargetIsLogical and opr.Right.TargetIsLogical))) then
|
|
begin
|
|
Error('Non-logical operand specified for logical operator');
|
|
Inc(errCount);
|
|
end;
|
|
end else
|
|
begin
|
|
opr.Right.TargetDecl := opr.Right.DeclType;
|
|
if (opr.IsArithmetic and (not opr.Right.TargetIsNumeric)) then
|
|
begin
|
|
Error('Non-numeric operand specified for unary minus');
|
|
Inc(errCount);
|
|
end;
|
|
if (opr.IsLogical and (not opr.Right.TargetIsLogical)) then
|
|
begin
|
|
Error('Non-logical operand specified for NOT');
|
|
Inc(errCount);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function Reduce(opr: TExprOperator): TExpressionTerm;
|
|
// Reduce expressions consisting of only literals to a single literal.
|
|
var
|
|
lint, rint, irslt, i: Integer;
|
|
lflt, rflt, rrslt: Double;
|
|
tgt: TDeclType;
|
|
lit: TExprLiteral;
|
|
iff: TExprIf;
|
|
stemp: AnsiString;
|
|
begin
|
|
Result := opr;
|
|
irslt := 0;
|
|
rrslt := 0;
|
|
if (opr is TExprIf) then
|
|
begin
|
|
// Special processing for IF expressions
|
|
iff := TExprIf(opr);
|
|
if ((iff.ThenExpr.TargetDecl = dtReal) and (iff.ElseExpr.TargetDecl = dtInteger)) then
|
|
iff.ElseExpr.TargetDecl := dtReal;
|
|
if ((iff.ThenExpr.TargetDecl = dtInteger) and (iff.ElseExpr.TargetDecl = dtReal)) then
|
|
iff.ThenExpr.TargetDecl := dtReal;
|
|
iff.TargetDecl := iff.ThenExpr.TargetDecl;
|
|
Exit;
|
|
end;
|
|
|
|
if ((not Assigned(opr.Left)) and (not Assigned(opr.Right))) then
|
|
Exit;
|
|
if (Assigned(opr.Left)) then
|
|
begin
|
|
if (opr.Left is TExprLiteral) then
|
|
begin
|
|
if ((opr.Left.TargetDecl = dtReal) and (opr.Left.DeclType = dtInteger)) then
|
|
begin
|
|
stemp := TExprLiteral(opr.Left).Literal.Value + '.0';
|
|
if (stemp[1] = '.') then
|
|
stemp := '0' + stemp;
|
|
i := FLiterals.Add(dtReal, stemp);
|
|
Dec(TExprLiteral(opr.Left).Literal.RefCount);
|
|
TExprLiteral(opr.Left).Literal := FLiterals[i];
|
|
end;
|
|
end;
|
|
if (opr.Right is TExprLiteral) then
|
|
begin
|
|
if ((opr.Right.TargetDecl = dtReal) and (opr.Right.DeclType = dtInteger)) then
|
|
begin
|
|
stemp := TExprLiteral(opr.Right).Literal.Value + '.0';
|
|
if (stemp[1] = '.') then
|
|
stemp := '0' + stemp;
|
|
i := FLiterals.Add(dtReal, stemp);
|
|
Dec(TExprLiteral(opr.Right).Literal.RefCount);
|
|
TExprLiteral(opr.Right).Literal := FLiterals[i];
|
|
end;
|
|
end;
|
|
if ((not (opr.Left is TExprLiteral)) or (not (opr.Right is TExprLiteral))) then
|
|
Exit;
|
|
if ((opr.Left.DeclType = dtReal) or (opr.Right.DeclType = dtReal)) then
|
|
begin
|
|
tgt := dtReal;
|
|
if (not TryStrToFloat(String(TExprLiteral(opr.Left).Literal.Value), lflt)) then
|
|
lflt := 0.0;
|
|
if (not TryStrToFloat(String(TExprLiteral(opr.Right).Literal.Value), rflt)) then
|
|
rflt := 0.0;
|
|
end else
|
|
begin
|
|
tgt := dtInteger;
|
|
if (not TryStrToInt(String(TExprLiteral(opr.Left).Literal.Value), lint)) then
|
|
lint := 0;
|
|
if (not TryStrToInt(String(TExprLiteral(opr.Right).Literal.Value), rint)) then
|
|
rint := 0;
|
|
end;
|
|
end else
|
|
begin
|
|
if (not (opr.Right is TExprLiteral)) then
|
|
Exit;
|
|
if (opr.Right.DeclType = dtReal) then
|
|
begin
|
|
tgt := dtReal;
|
|
if (not TryStrToFloat(String(TExprLiteral(opr.Right).Literal.Value), rflt)) then
|
|
rflt := 0.0;
|
|
end else
|
|
begin
|
|
tgt := dtInteger;
|
|
if (not TryStrToInt(String(TExprLiteral(opr.Right).Literal.Value), rint)) then
|
|
rint := 0;
|
|
end;
|
|
end;
|
|
case opr.OType of
|
|
otExponent:
|
|
begin
|
|
if (tgt = dtReal) then
|
|
rrslt := Power(lflt, rflt)
|
|
else
|
|
rrslt := Power(lint, rint);
|
|
tgt := dtReal;
|
|
end;
|
|
otUnaryMinus:
|
|
begin
|
|
if (tgt = dtReal) then
|
|
rrslt := -rflt
|
|
else
|
|
irslt := -rint;
|
|
end;
|
|
otMultiply:
|
|
begin
|
|
if (tgt = dtReal) then
|
|
rrslt := lflt * rflt
|
|
else
|
|
irslt := lint * rint;
|
|
end;
|
|
otDivide:
|
|
begin
|
|
if (tgt = dtReal) then
|
|
rrslt := lflt / rflt
|
|
else
|
|
rrslt := lint / rint;
|
|
tgt := dtReal;
|
|
end;
|
|
otIntDivide:
|
|
begin
|
|
if (tgt = dtReal) then
|
|
irslt := 0
|
|
else
|
|
irslt := lint div rint;
|
|
end;
|
|
otPlus:
|
|
begin
|
|
if (tgt = dtReal) then
|
|
rrslt := lflt + rflt
|
|
else
|
|
irslt := lint + rint;
|
|
end;
|
|
otMinus:
|
|
begin
|
|
if (tgt = dtReal) then
|
|
rrslt := lflt - rflt
|
|
else
|
|
irslt := lint - rint;
|
|
end;
|
|
otLess:
|
|
begin
|
|
if (tgt = dtReal) then
|
|
irslt := Integer(lflt < rflt)
|
|
else
|
|
irslt := Integer(lint < rint);
|
|
tgt := dtLogical;
|
|
end;
|
|
otLessEqual:
|
|
begin
|
|
if (tgt = dtReal) then
|
|
irslt := Integer(lflt <= rflt)
|
|
else
|
|
irslt := Integer(lint <= rint);
|
|
tgt := dtLogical;
|
|
end;
|
|
otGreater:
|
|
begin
|
|
if (tgt = dtReal) then
|
|
irslt := Integer(lflt > rflt )
|
|
else
|
|
irslt := Integer(lint > rint);
|
|
tgt := dtLogical;
|
|
end;
|
|
otGreaterEqual:
|
|
begin
|
|
if (tgt = dtReal) then
|
|
irslt := Integer(lflt >= rflt)
|
|
else
|
|
irslt := Integer(lint >= rint);
|
|
tgt := dtLogical;
|
|
end;
|
|
otEqual:
|
|
begin
|
|
if (tgt = dtReal) then
|
|
irslt := Integer(lflt = rflt )
|
|
else
|
|
irslt := Integer(lint = rint);
|
|
tgt := dtLogical;
|
|
end;
|
|
otNotEqual:
|
|
begin
|
|
if (tgt = dtReal) then
|
|
irslt := Integer(lflt <> rflt)
|
|
else
|
|
irslt := Integer(lint <> rint);
|
|
tgt := dtLogical;
|
|
end;
|
|
otNot:
|
|
begin
|
|
if (tgt = dtReal) then
|
|
irslt := 0
|
|
else
|
|
begin
|
|
if (rint = 0) then
|
|
rint := 1
|
|
else
|
|
rint := 0;
|
|
end;
|
|
tgt := dtLogical;
|
|
end;
|
|
otAnd:
|
|
begin
|
|
if (tgt = dtReal) then
|
|
irslt := 0
|
|
else
|
|
begin
|
|
if ((lint <> 0) and (rint <> 0)) then
|
|
irslt := 1
|
|
else
|
|
irslt := 0;
|
|
end;
|
|
tgt := dtLogical;
|
|
end;
|
|
otOr:
|
|
begin
|
|
if (tgt = dtReal) then
|
|
irslt := 0
|
|
else
|
|
begin
|
|
if ((lint <> 0) or (rint <> 0)) then
|
|
irslt := 1
|
|
else
|
|
irslt := 0;
|
|
end;
|
|
tgt := dtLogical;
|
|
end;
|
|
otXor:
|
|
begin
|
|
if (tgt = dtReal) then
|
|
irslt := 0
|
|
else
|
|
begin
|
|
if (((lint <> 0) and (rint <> 0)) or ((lint = 0) and (rint = 0))) then
|
|
irslt := 0
|
|
else
|
|
irslt := 1;
|
|
end;
|
|
tgt := dtLogical;
|
|
end;
|
|
otImpl:
|
|
begin
|
|
if (tgt = dtReal) then
|
|
irslt := 0
|
|
else
|
|
begin
|
|
if ((lint <> 0) and (rint = 0)) then
|
|
irslt := 0
|
|
else
|
|
irslt := 1;
|
|
end;
|
|
tgt := dtLogical;
|
|
end;
|
|
otEquiv:
|
|
begin
|
|
if (tgt = dtReal) then
|
|
irslt := 0
|
|
else
|
|
begin
|
|
if (((lint <> 0) and (rint <> 0)) or ((lint = 0) and (rint = 0))) then
|
|
irslt := 1
|
|
else
|
|
irslt := 0;
|
|
end;
|
|
tgt := dtLogical;
|
|
end;
|
|
end;
|
|
|
|
if (tgt = dtReal) then
|
|
begin
|
|
stemp := AnsiString(FloatToStr(rrslt));
|
|
i := FLiterals.Add(dtReal, stemp);
|
|
lit := TExprLiteral.Create;
|
|
lit.TargetDecl := dtReal;
|
|
lit.Literal := FLiterals[i];
|
|
end else if (tgt = dtInteger) then
|
|
begin
|
|
stemp := AnsiString(IntToStr(irslt));
|
|
i := FLiterals.Add(dtInteger, stemp);
|
|
lit := TExprLiteral.Create;
|
|
lit.TargetDecl := dtInteger;
|
|
lit.Literal := FLiterals[i];
|
|
end else if (tgt = dtLogical) then
|
|
begin
|
|
if (irslt = 0) then
|
|
stemp := '0'
|
|
else
|
|
stemp := '1';
|
|
i := FLiterals.Add(dtLogical, stemp);
|
|
lit := TExprLiteral.Create;
|
|
lit.TargetDecl := dtLogical;
|
|
lit.Literal := FLiterals[i];
|
|
end else
|
|
begin
|
|
lit := nil;
|
|
end;
|
|
if (Assigned(opr.Left)) then
|
|
Dec(TExprLiteral(opr.Left).Literal.RefCount);
|
|
Dec(TExprLiteral(opr.Right).Literal.RefCount);
|
|
opr.Free;
|
|
Result := lit;
|
|
end;
|
|
|
|
function Factor(priorityLevel, condDepth: Integer; var errCount: Integer): TExpressionTerm;
|
|
var
|
|
ttype: TTokenType;
|
|
token: AnsiString;
|
|
done: Boolean;
|
|
opr: TExprOperator;
|
|
left, right: TExpressionTerm;
|
|
ot: TExprOperatorType;
|
|
begin
|
|
if (FTokenTrace) then
|
|
WriteLn(Format('Factor (priority %s)', [priorityLevel]));
|
|
|
|
Result := nil;
|
|
left := nil;
|
|
right := nil;
|
|
opr := nil;
|
|
try
|
|
FTokenGen.Get(ttype, token);
|
|
if ((token = '-') or (token = 'NOT')) then
|
|
begin
|
|
if (PriorityMatch(token, priorityLevel)) then
|
|
begin
|
|
right := Factor(priorityLevel + 1, condDepth, errCount);
|
|
if (not Assigned(right)) then
|
|
Exit;
|
|
opr := TExprOperator.Create;
|
|
opr.Right := right;
|
|
opr.TargetDecl := right.DeclType;
|
|
if (token = '-') then
|
|
begin
|
|
opr.OType := otUnaryMinus;
|
|
if (not right.IsNumeric) then
|
|
begin
|
|
Error('Integer or real value expected for unary minus');
|
|
Inc(errCount);
|
|
end;
|
|
CheckOperands(opr, errCount);
|
|
Result := Reduce(opr);
|
|
end else
|
|
begin
|
|
opr.OType := otNot;
|
|
if (not right.IsLogical) then
|
|
begin
|
|
Error('Boolean value expected for NOT');
|
|
Inc(errCount);
|
|
end;
|
|
CheckOperands(opr, errCount);
|
|
Result := Reduce(opr);
|
|
end;
|
|
Exit;
|
|
end else
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
end;
|
|
end else
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
end;
|
|
|
|
if (priorityLevel >= High(ExprOprPriorities)) then
|
|
left := Operand(errCount, condDepth)
|
|
else
|
|
left := Factor(priorityLevel + 1, condDepth, errCount);
|
|
if (not Assigned(left)) then
|
|
Exit;
|
|
|
|
done := False;
|
|
while (not done) do
|
|
begin
|
|
FTokenGen.Get(ttype, token);
|
|
ot := OperatorType(token);
|
|
if (ot <> otUnknown) then
|
|
begin
|
|
if (PriorityMatch(token, priorityLevel)) then
|
|
begin
|
|
if (priorityLevel >= High(ExprOprPriorities)) then
|
|
right := Operand(errCount, condDepth)
|
|
else
|
|
right := Factor(priorityLevel + 1, condDepth, errCount);
|
|
if (not Assigned(right)) then
|
|
begin
|
|
FreeAndNil(left);
|
|
Exit;
|
|
end;
|
|
opr := TExprOperator.Create;
|
|
opr.OType := ot;
|
|
opr.Left := left;
|
|
opr.Right := right;
|
|
CheckOperands(opr, errCount);
|
|
left := Reduce(opr);
|
|
end else
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
done := True;
|
|
end;
|
|
end else
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
done := True;
|
|
end;
|
|
end;
|
|
if ((priorityLevel = 0) and (left is TExprIf)) then
|
|
Reduce(TExprIf(left));
|
|
Result := left;
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
Error(E.Message);
|
|
FreeAndNil(left);
|
|
FreeAndNil(right);
|
|
FreeAndNil(opr);
|
|
Result := nil;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function PostFix(term: TExpressionTerm): AnsiString;
|
|
// Traverse the expression tree and generate the eqivalent postfix
|
|
// expression. Used for debugging purposes only.
|
|
begin
|
|
if (not Assigned(term)) then
|
|
begin
|
|
Result := '';
|
|
end else if (term is TExprLiteral) then
|
|
begin
|
|
Result := TExprLiteral(term).Literal.Value + ' ';
|
|
end else if (term is TExprVariable) then
|
|
begin
|
|
Result := TExprVariable(term).Symbol.ID + ' ';
|
|
end else if (term is TExprIf) then
|
|
begin
|
|
Result := PostFix(TExprIf(term).IfExpr) +
|
|
oprs[otIf] + ' ' +
|
|
PostFix(TExprIf(term).ThenExpr) +
|
|
oprs[otThen] + ' ' +
|
|
PostFix(TExprIf(term).ElseExpr);
|
|
if (TExprIf(term).ConditionalDepth = 0) then
|
|
Result := Result + oprs[otElse] + ' ';
|
|
end else if (term is TExprOperator) then
|
|
begin
|
|
Result := PostFix(TExprOperator(term).Left) +
|
|
PostFix(TExprOperator(term).Right) +
|
|
oprs[TExprOperator(term).OType] + ' ';
|
|
end;
|
|
end;
|
|
|
|
var
|
|
post: AnsiString;
|
|
ttype: TTokenType;
|
|
token: AnsiString;
|
|
errCount: Integer;
|
|
begin
|
|
Result := nil;
|
|
FTokenGen.Get(ttype, token);
|
|
if (not ((ttype = ttIdentifier) or (ttype = ttInteger) or (ttype = ttReal) or
|
|
(ttype = ttLogicalValue) or (ttype = ttString) or // (ttype = ttLabel) or
|
|
(ttype = ttIf) or (ttype = ttLeftParen) or (token = '-') or
|
|
(token = 'NOT'))) then
|
|
Exit;
|
|
|
|
FTokenGen.Unget(ttype, token);
|
|
try
|
|
errCount := 0;
|
|
Result := TExpression(Factor(0, 0, errCount));
|
|
if (Assigned(Result)) then
|
|
begin
|
|
Result.ErrorCount := errCount;
|
|
// **********************
|
|
// for debugging
|
|
post := PostFix(Result);
|
|
// **********************
|
|
end;
|
|
except
|
|
on E: Exception do
|
|
begin
|
|
Error(E.Message);
|
|
FreeAndNil(Result);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCompiler.FindSymbol(id: AnsiString; st: TSymbolType): TSymbol;
|
|
var
|
|
blocks: TArray<TBlock>;
|
|
i: Integer;
|
|
begin
|
|
Result := nil;
|
|
blocks := FBlocks.ToArray;
|
|
for i := High(blocks) downto Low(blocks) do
|
|
begin
|
|
if (blocks[i].Symbols.TryGetValue(id, st, Result)) then
|
|
Exit;
|
|
end;
|
|
end;
|
|
|
|
function TCompiler.Forr: Boolean;
|
|
var
|
|
ttype: TTokenType;
|
|
token: AnsiString;
|
|
expr: TExpression;
|
|
stmt: TForStatement;
|
|
asgType: TDeclType;
|
|
mult, minus, gt, sign: TExprOperator;
|
|
lv: TExprVariable;
|
|
zero: TExprLiteral;
|
|
i: Integer;
|
|
begin
|
|
asgType := dtUnknown;
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype <> ttFor) then
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
Result := False;
|
|
Exit;
|
|
end;
|
|
|
|
Result := True;
|
|
stmt := TForStatement.Create;
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype <> ttIdentifier) then
|
|
begin
|
|
Error(Format('Identifier expected, got %s', [token]));
|
|
FTokenGen.Unget(ttype, token);
|
|
end;
|
|
stmt.LoopVar := FindSymbol(token, stVariable);
|
|
if (not Assigned(stmt.LoopVar)) then
|
|
begin
|
|
Error(Format('%s is undefined', [token]));
|
|
FTokenGen.Unget(ttype, token);
|
|
end;
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype <> ttAssignment) then
|
|
begin
|
|
Error(Format('Assignment expected, got %s', [token]));
|
|
FTokenGen.Unget(ttype, token);
|
|
end;
|
|
// I := e1[, e2[, e3 ...]]]
|
|
Inc(FSymbolNum);
|
|
stmt.LoopLblNum := FSymbolNum;
|
|
Inc(FSymbolNum);
|
|
stmt.EndLblNum := FSymbolNum;
|
|
expr := Expression;
|
|
if (Assigned(expr)) then
|
|
begin
|
|
stmt.AsgExpressions.Add(expr);
|
|
asgType := expr.TargetDecl;
|
|
end else
|
|
Error(Format('Expression expected after assignment', [token]));
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype = ttComma) then
|
|
begin
|
|
repeat
|
|
expr := Expression;
|
|
if (Assigned(expr)) then
|
|
begin
|
|
if (expr.TargetDecl <> stmt.LoopVar.DeclType) then
|
|
Error('All expression must be same type as loop variable');
|
|
stmt.AsgExpressions.Add(expr);
|
|
end else
|
|
Error('Expression expected after comma');
|
|
FTokenGen.Get(ttype, token);
|
|
until ttype <> ttComma;
|
|
FTokenGen.Unget(ttype, token);
|
|
end else
|
|
FTokenGen.Unget(ttype, token);
|
|
if (stmt.LoopVar.DeclType <> asgType) then
|
|
Error('Loop variable type and expression type must match');
|
|
// DO, STEP or WHILE
|
|
FTokenGen.Get(ttype, token);
|
|
if (token = 'DO') then
|
|
begin
|
|
FCodeGen.ForListInit(stmt);
|
|
if (not Statement) then
|
|
Error('Statement expected following DO');
|
|
FCodeGen.ForListEnd(stmt);
|
|
end else
|
|
begin
|
|
if (token = 'STEP') then
|
|
begin
|
|
// STEP
|
|
stmt.IncrExpression := Expression;
|
|
if (Assigned(stmt.IncrExpression)) then
|
|
begin
|
|
if (stmt.IncrExpression.TargetDecl <> asgType) then
|
|
Error('Assignment and STEP expressions must the same type');
|
|
end else
|
|
Error('Expression expected following STEP');
|
|
// UNTIL
|
|
FTokenGen.Get(ttype, token);
|
|
if (token <> 'UNTIL') then
|
|
begin
|
|
Error(Format('UNTIL expected, got %s', [token]));
|
|
FTokenGen.Unget(ttype, token);
|
|
end;
|
|
expr := Expression;
|
|
if (Assigned(expr)) then
|
|
begin
|
|
if (expr.TargetDecl <> asgType) then
|
|
Error('Assignment and UNTIL expressions must be the same type');
|
|
end else
|
|
Error('Expression expected following UNTIL');
|
|
// Generate test expression (SIGN(step) * (LoopVar - TestExpression)) > 0
|
|
lv := TExprVariable.Create;
|
|
lv.Symbol := stmt.LoopVar;
|
|
lv.TargetDecl := stmt.LoopVar.DeclType;
|
|
//
|
|
sign := TExprOperator.Create;
|
|
sign.OType := otSign;
|
|
sign.TargetDecl := stmt.LoopVar.DeclType;
|
|
sign.Right := stmt.IncrExpression;
|
|
//
|
|
minus := TExprOperator.Create;
|
|
minus.OType := otMinus;
|
|
minus.TargetDecl := stmt.LoopVar.DeclType;
|
|
minus.Left := lv;
|
|
minus.Right := expr;
|
|
//
|
|
mult := TExprOperator.Create;
|
|
mult.OType := otMultiply;
|
|
mult.TargetDecl := stmt.LoopVar.DeclType;
|
|
mult.Left := sign;
|
|
mult.Right := minus;
|
|
//
|
|
zero := TExprLiteral.Create;
|
|
i := FLiterals.Add(stmt.LoopVar.DeclType, '0');
|
|
zero.Literal := FLiterals[i];
|
|
zero.TargetDecl := stmt.LoopVar.DeclType;
|
|
//
|
|
gt := TExprOperator.Create;
|
|
gt.OType := otGreater;
|
|
gt.TargetDecl := dtLogical;
|
|
gt.Left := mult;
|
|
gt.Right := zero;
|
|
//
|
|
stmt.TestExpression := TExpression(gt);
|
|
FCodeGen.ForStep(stmt);
|
|
FTokenGen.Get(ttype, token);
|
|
if (token <> 'DO') then
|
|
begin
|
|
Error(Format('Expected DO or WHILE, got %s', [token]));
|
|
FTokenGen.Unget(ttype, token);
|
|
end;
|
|
if (not Statement) then
|
|
Error('Statement expected following DO');
|
|
FCodeGen.ForStepIncr(stmt);
|
|
end else if (token = 'WHILE') then
|
|
begin
|
|
// WHILE
|
|
stmt.TestExpression := Expression;
|
|
if (Assigned(stmt.TestExpression)) then
|
|
begin
|
|
if (stmt.TestExpression.TargetDecl <> dtLogical) then
|
|
Error('Boolean expression expected following WHILE');
|
|
end else
|
|
Error('Expression expected following WHILE');
|
|
FTokenGen.Get(ttype, token);
|
|
if (token <> 'DO') then
|
|
begin
|
|
Error(Format('Expected DO or WHILE, got %s', [token]));
|
|
FTokenGen.Unget(ttype, token);
|
|
end;
|
|
FCodeGen.ForWhile(stmt);
|
|
if (not Statement) then
|
|
Error('Statement expected following DO');
|
|
FCodeGen.ForWhileEnd(stmt);
|
|
end else
|
|
begin
|
|
Error(Format('Expected DO, STEP or WHILE, got %s', [token]));
|
|
FTokenGen.Unget(ttype, token);
|
|
end;
|
|
end;
|
|
|
|
stmt.Free;
|
|
end;
|
|
|
|
function TCompiler.Gotoo: Boolean;
|
|
var
|
|
ttype: TTokenType;
|
|
token: AnsiString;
|
|
gt: TGotoStatement;
|
|
begin
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype <> ttGoto) then
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
Result := False;
|
|
Exit;
|
|
end;
|
|
|
|
Result := True;
|
|
gt := TGotoStatement.Create;
|
|
gt.Expr := DesignationalExpr;
|
|
if (Assigned(gt.Expr)) then
|
|
FCodeGen.Gotoo(gt.Expr)
|
|
else
|
|
Error('Designational expression expected following GOTO');
|
|
gt.Free;
|
|
end;
|
|
|
|
function TCompiler.Iff: Boolean;
|
|
var
|
|
ttype: TTokenType;
|
|
token: AnsiString;
|
|
stmt: TIfStatement;
|
|
begin
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype <> ttIf) then
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
Result := False;
|
|
Exit;
|
|
end;
|
|
|
|
Result := True;
|
|
stmt := TIfStatement.Create;
|
|
stmt.Expr := Expression;
|
|
if (stmt.Expr.TargetDecl <> dtLogical) then
|
|
begin
|
|
Error('Boolean expression expected following IF');
|
|
Exit;
|
|
end;
|
|
FTokenGen.Get(ttype, token);
|
|
if (token <> 'THEN') then
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
Error(Format('THEN expected, got %s', [token]));
|
|
end;
|
|
Inc(FSymbolNum);
|
|
stmt.ElseLblNum := FSymbolNum;
|
|
Inc(FSymbolNum);
|
|
stmt.EndLblNum := FSymbolNum;
|
|
FCodeGen.IfThen(stmt);
|
|
if (not Statement) then
|
|
begin
|
|
Error('Statement expected following THEN');
|
|
Exit;
|
|
end;
|
|
FCodeGen.IfElse(stmt);
|
|
FTokenGen.Get(ttype, token);
|
|
if (token = 'ELSE') then
|
|
begin
|
|
if (not Statement) then
|
|
Error('Statement expected');
|
|
end else
|
|
FTokenGen.Unget(ttype, token);
|
|
FCodeGen.IfEnd(stmt);
|
|
|
|
stmt.Free;
|
|
end;
|
|
|
|
function TCompiler.Labell: Boolean;
|
|
var
|
|
ttype: TTokenType;
|
|
token: AnsiString;
|
|
b: TBlock;
|
|
sym: TSymbol;
|
|
begin
|
|
b := FBlocks.Peek;
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype = ttLabel) then
|
|
begin
|
|
sym := NewSymbol(token, stLabel, False);
|
|
if (b.Symbols.TryGetValue(token, stLabel, sym)) then
|
|
begin
|
|
if (sym.IsForward) then
|
|
begin
|
|
sym.IsForward := False;
|
|
FCodeGen.Labell(sym);
|
|
end else
|
|
Error(Format('Duplicate label %s', [token]));
|
|
end else
|
|
begin
|
|
sym := NewSymbol(token, stLabel, False);
|
|
b.Symbols.Add(token, sym);
|
|
FCodeGen.Labell(sym);
|
|
end;
|
|
Result := True;
|
|
end else
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
Result := False;
|
|
end;
|
|
end;
|
|
|
|
function TCompiler.NewBlock(isPgm, isMain, isProc: Boolean): TBlock;
|
|
begin
|
|
Result := TBlock.Create;
|
|
Result.IsMain := isMain;
|
|
Result.IsProc := isProc;
|
|
Result.IsProgram := ispgm;
|
|
Inc(FBlockNum);
|
|
Result.BlockNum := FBlockNum;
|
|
end;
|
|
|
|
function TCompiler.NewSymbol(id: AnsiString; st: TSymbolType; isArray: Boolean): TSymbol;
|
|
begin
|
|
if (st = stSwitch) then
|
|
Result := TSwitchSymbol.Create
|
|
else
|
|
Result := TSymbol.Create;
|
|
Result.ID := id;
|
|
Result.SymbolType := st;
|
|
Result.IsArray := isArray;
|
|
Inc(FSymbolNum);
|
|
Result.SymbolNum := FSymbolNum;
|
|
end;
|
|
|
|
function TCompiler.ProcedureDecl: Boolean;
|
|
var
|
|
ttypeDecl, ttypeProc: TTokenType;
|
|
tokenDecl, tokenProc: AnsiString;
|
|
begin
|
|
FTokenGen.Get(ttypeDecl, tokenDecl);
|
|
if (ttypeDecl = ttComment) then
|
|
begin
|
|
FTokenGen.FlushLine;
|
|
Result := True;
|
|
Exit;
|
|
end else if ((ttypeDecl = ttDeclarator) and
|
|
((tokenDecl = 'INTEGER') or (tokenDecl = 'REAL'))) then
|
|
begin
|
|
FTokenGen.Get(ttypeProc, tokenProc);
|
|
if ((ttypeProc <> ttDeclarator) or (tokenProc <> 'PROCEDURE')) then
|
|
begin
|
|
Result := False;
|
|
FTokenGen.Unget(ttypeProc, tokenProc);
|
|
FTokenGen.Unget(ttypeDecl, tokenDecl);
|
|
Exit;
|
|
end;
|
|
end else
|
|
begin
|
|
if ((ttypeDecl <> ttDeclarator) or (tokenDecl <> 'PROCEDURE')) then
|
|
begin
|
|
Result := False;
|
|
FTokenGen.Unget(ttypeDecl, tokenDecl);
|
|
Exit;
|
|
end;
|
|
ttypeDecl := ttUnknown;
|
|
end;
|
|
|
|
Result := True;
|
|
end;
|
|
|
|
function TCompiler.Programm: Boolean;
|
|
var
|
|
b: TBlock;
|
|
begin
|
|
b := NewBlock(True, False, False);
|
|
FBlocks.Push(b);
|
|
Result := Block(True, True, False);
|
|
if (not Result) then
|
|
FBlocks.Pop.Free;
|
|
end;
|
|
|
|
function TCompiler.Statement: Boolean;
|
|
var
|
|
ttype: TTokenType;
|
|
token: AnsiString;
|
|
parent: TBlock;
|
|
begin
|
|
Result := False;
|
|
|
|
while (Labell) do
|
|
;
|
|
|
|
FTokenGen.Get(ttype, token);
|
|
case ttype of
|
|
ttEof:
|
|
begin
|
|
Result := False;
|
|
Error('Unexpected enf-of-file');
|
|
end;
|
|
ttLineSeparator:
|
|
begin
|
|
Result := True;
|
|
end;
|
|
ttEnd:
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
Result := False;
|
|
end;
|
|
ttComment:
|
|
begin
|
|
FTokenGen.FlushLine;
|
|
Result := True;
|
|
end;
|
|
ttIdentifier:
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
Result := Assignment(0, dtUnknown);
|
|
end;
|
|
ttBegin:
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
parent := FBlocks.Peek;
|
|
Result := Block(False, False, parent.IsProc);
|
|
end
|
|
else
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
if (Gotoo) then
|
|
Result := True
|
|
else if (Iff) then
|
|
Result := True
|
|
else if (Forr) then
|
|
Result := True
|
|
else if (Write) then
|
|
Result := True;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TCompiler.Write: Boolean;
|
|
var
|
|
ttype, ttype2: TTokenType;
|
|
token, token2: AnsiString;
|
|
expr: TExpression;
|
|
arr: TExprArray;
|
|
sym: TSymbol;
|
|
wr: TWriteStatement;
|
|
begin
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype <> ttWrite) then
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
Result := False;
|
|
Exit;
|
|
end;
|
|
|
|
Result := True;
|
|
wr := TWriteStatement.Create;
|
|
try
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype <> ttLeftParen) then
|
|
Error(Format('Left bracket expected, got %s', [token]));
|
|
FTokenGen.Get(ttype, token);
|
|
if (token = 'PRINTER') then
|
|
wr.Device := iodPrinter
|
|
else if (token = 'PUNCH') then
|
|
wr.Device := iodPunch
|
|
else if (token = 'CONSOLE') then
|
|
wr.Device := iodConsole
|
|
else
|
|
FTokenGen.Unget(ttype, token);
|
|
if (wr.Device <> iodUnknown) then
|
|
begin
|
|
// Trash comma following device name, if present
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype <> ttComma) then
|
|
FTokenGen.Unget(ttype, token);
|
|
end else
|
|
// Default device to printer
|
|
wr.Device := iodPrinter;
|
|
repeat
|
|
// We need to look ahead a bit here to see if the parameter is
|
|
// an array with no subscripts.
|
|
FTokenGen.Get(ttype, token);
|
|
if (ttype = ttIdentifier) then
|
|
begin
|
|
sym := FindSymbol(token, stVariable);
|
|
if (Assigned(sym) and (sym.IsArray)) then
|
|
begin
|
|
FTokenGen.Get(ttype2, token2);
|
|
if (ttype2 <> ttLeftParen) then
|
|
begin
|
|
arr := TExprArray.Create;
|
|
arr.Symbol := sym;
|
|
arr.TargetDecl := sym.DeclType;
|
|
wr.Params.Add(arr);
|
|
ttype := ttype2;
|
|
token := token2;
|
|
Continue;
|
|
end else
|
|
FTokenGen.Unget(ttype2, token2);
|
|
end;
|
|
end;
|
|
FTokenGen.Unget(ttype, token);
|
|
expr := Expression;
|
|
if (Assigned(expr)) then
|
|
wr.Params.Add(expr)
|
|
else
|
|
begin
|
|
FTokenGen.Unget(ttype, token);
|
|
Error('Expression or array expected for WRITE');
|
|
end;
|
|
FTokenGen.Get(ttype, token);
|
|
until ttype <> ttComma;
|
|
if (ttype <> ttRightParen) then
|
|
begin
|
|
Error(Format('Right bracket expected, got %s', [token]));
|
|
FTokenGen.Unget(ttype, token);
|
|
end;
|
|
FCodeGen.Write(wr);
|
|
finally
|
|
wr.Free;
|
|
end;
|
|
end;
|
|
|
|
end.
|