| 1 | program compiler;
|
|---|
| 2 |
|
|---|
| 3 | {$APPTYPE CONSOLE}
|
|---|
| 4 |
|
|---|
| 5 | {$R *.res}
|
|---|
| 6 |
|
|---|
| 7 | uses
|
|---|
| 8 | System.SysUtils, System.Classes, System.Generics.Collections;
|
|---|
| 9 |
|
|---|
| 10 | const
|
|---|
| 11 | NewLine = #13#10;
|
|---|
| 12 |
|
|---|
| 13 | type
|
|---|
| 14 | TStatement = class
|
|---|
| 15 | end;
|
|---|
| 16 |
|
|---|
| 17 | TBeginEnd = class(TStatement)
|
|---|
| 18 | Items: TObjectList<TStatement>;
|
|---|
| 19 | constructor Create;
|
|---|
| 20 | destructor Destroy; override;
|
|---|
| 21 | end;
|
|---|
| 22 |
|
|---|
| 23 | TFunctionCall = class(TStatement)
|
|---|
| 24 | Name: string;
|
|---|
| 25 | end;
|
|---|
| 26 |
|
|---|
| 27 | TProgram = class
|
|---|
| 28 | Main: TBeginEnd;
|
|---|
| 29 | procedure Clear;
|
|---|
| 30 | constructor Create;
|
|---|
| 31 | destructor Destroy; override;
|
|---|
| 32 | end;
|
|---|
| 33 |
|
|---|
| 34 | TCompiler = class
|
|---|
| 35 | private
|
|---|
| 36 | Functions: TStringList;
|
|---|
| 37 | SourcePos: Integer;
|
|---|
| 38 | Indentation: Integer;
|
|---|
| 39 | function IsAlphaNumeric(C: Char): Boolean;
|
|---|
| 40 | function IsSpecialSymbol(C: Char): Boolean;
|
|---|
| 41 | function IsWhiteSpace(C: Char): Boolean;
|
|---|
| 42 | function ReadNext: string;
|
|---|
| 43 | function CheckNext(Text: string): Boolean;
|
|---|
| 44 | function Expect(Text: string): Boolean;
|
|---|
| 45 | function ParseBeginEnd(var Block: TBeginEnd): Boolean;
|
|---|
| 46 | function ParseProgram(var Prog: TProgram): Boolean;
|
|---|
| 47 | function ParseStatement(var Statement: TStatement): Boolean;
|
|---|
| 48 | function ParseFunctionCall(var FuncCall: TFunctionCall): Boolean;
|
|---|
| 49 | procedure ErrorMsg(Text: string);
|
|---|
| 50 | procedure Emit(Text: string);
|
|---|
| 51 |
|
|---|
| 52 | procedure GenerateProgram(Prog: TProgram);
|
|---|
| 53 | procedure GenerateBeginEnd(BeginEnd: TBeginEnd);
|
|---|
| 54 | procedure GenerateStatement(Statement: TStatement);
|
|---|
| 55 | procedure GenerateFunctionCall(FuncCall: TFunctionCall);
|
|---|
| 56 | function GetInd: string;
|
|---|
| 57 | public
|
|---|
| 58 | Prog: TProgram;
|
|---|
| 59 | Source: string;
|
|---|
| 60 | Destination: string;
|
|---|
| 61 | procedure Compile;
|
|---|
| 62 | constructor Create;
|
|---|
| 63 | destructor Destroy; override;
|
|---|
| 64 | end;
|
|---|
| 65 |
|
|---|
| 66 | { TCompiler }
|
|---|
| 67 |
|
|---|
| 68 | function TCompiler.CheckNext(Text: string): Boolean;
|
|---|
| 69 | var
|
|---|
| 70 | P: Integer;
|
|---|
| 71 | Token: string;
|
|---|
| 72 | begin
|
|---|
| 73 | P := SourcePos;
|
|---|
| 74 | Token := ReadNext;
|
|---|
| 75 | Result := Token = Text;
|
|---|
| 76 | SourcePos := P;
|
|---|
| 77 | end;
|
|---|
| 78 |
|
|---|
| 79 | procedure TCompiler.Compile;
|
|---|
| 80 | begin
|
|---|
| 81 | SourcePos := 1;
|
|---|
| 82 | Destination := '';
|
|---|
| 83 | if Assigned(Prog) then
|
|---|
| 84 | FreeAndNil(Prog);
|
|---|
| 85 | ParseProgram(Prog);
|
|---|
| 86 | GenerateProgram(Prog);
|
|---|
| 87 | end;
|
|---|
| 88 |
|
|---|
| 89 | constructor TCompiler.Create;
|
|---|
| 90 | begin
|
|---|
| 91 | Prog := TProgram.Create;
|
|---|
| 92 | Functions := TStringList.Create;
|
|---|
| 93 | Functions.Add('WriteLn');
|
|---|
| 94 | end;
|
|---|
| 95 |
|
|---|
| 96 | destructor TCompiler.Destroy;
|
|---|
| 97 | begin
|
|---|
| 98 | FreeAndNil(Prog);
|
|---|
| 99 | FreeAndNil(Functions);
|
|---|
| 100 | inherited;
|
|---|
| 101 | end;
|
|---|
| 102 |
|
|---|
| 103 | procedure TCompiler.Emit(Text: string);
|
|---|
| 104 | begin
|
|---|
| 105 | Destination := Destination + Text;
|
|---|
| 106 | end;
|
|---|
| 107 |
|
|---|
| 108 | procedure TCompiler.ErrorMsg(Text: string);
|
|---|
| 109 | begin
|
|---|
| 110 | WriteLn('Error: ' + Text);
|
|---|
| 111 | end;
|
|---|
| 112 |
|
|---|
| 113 | function TCompiler.Expect(Text: string): Boolean;
|
|---|
| 114 | var
|
|---|
| 115 | Token: string;
|
|---|
| 116 | begin
|
|---|
| 117 | Token := ReadNext;
|
|---|
| 118 | if Token <> Text then
|
|---|
| 119 | ErrorMsg('Expected ' + Text + ' but ' + Token + ' found');
|
|---|
| 120 | Result := Token = Text;
|
|---|
| 121 | end;
|
|---|
| 122 |
|
|---|
| 123 | procedure TCompiler.GenerateBeginEnd(BeginEnd: TBeginEnd);
|
|---|
| 124 | var
|
|---|
| 125 | I: Integer;
|
|---|
| 126 | begin
|
|---|
| 127 | Emit(GetInd + 'begin' + NewLine);
|
|---|
| 128 | Inc(Indentation);
|
|---|
| 129 | for I := 0 to BeginEnd.Items.Count - 1 do begin
|
|---|
| 130 | GenerateStatement(BeginEnd.Items[I]);
|
|---|
| 131 | Emit(';' + NewLine);
|
|---|
| 132 | end;
|
|---|
| 133 | Dec(Indentation);
|
|---|
| 134 | Emit(GetInd + 'end');
|
|---|
| 135 | end;
|
|---|
| 136 |
|
|---|
| 137 | procedure TCompiler.GenerateFunctionCall(FuncCall: TFunctionCall);
|
|---|
| 138 | begin
|
|---|
| 139 | Emit(GetInd + FuncCall.Name);
|
|---|
| 140 | end;
|
|---|
| 141 |
|
|---|
| 142 | procedure TCompiler.GenerateProgram(Prog: TProgram);
|
|---|
| 143 | begin
|
|---|
| 144 | Indentation := 0;
|
|---|
| 145 | GenerateBeginEnd(Prog.Main);
|
|---|
| 146 | Emit('.' + NewLine);
|
|---|
| 147 | end;
|
|---|
| 148 |
|
|---|
| 149 | procedure TCompiler.GenerateStatement(Statement: TStatement);
|
|---|
| 150 | begin
|
|---|
| 151 | if Statement is TBeginEnd then GenerateBeginEnd(Statement as TBeginEnd)
|
|---|
| 152 | else if Statement is TFunctionCall then GenerateFunctionCall(Statement as TFunctionCall)
|
|---|
| 153 | else ErrorMsg('Unsuported statement type');
|
|---|
| 154 | end;
|
|---|
| 155 |
|
|---|
| 156 | function TCompiler.GetInd: string;
|
|---|
| 157 | begin
|
|---|
| 158 | REsult := StringOfChar(' ', Indentation * 2);
|
|---|
| 159 | end;
|
|---|
| 160 |
|
|---|
| 161 | function TCompiler.IsAlphaNumeric(C: Char): Boolean;
|
|---|
| 162 | begin
|
|---|
| 163 | Result := (C in ['a'..'z']) or (C in ['A'..'Z']) or (C in ['0'..'9'])
|
|---|
| 164 | end;
|
|---|
| 165 |
|
|---|
| 166 | function TCompiler.IsSpecialSymbol(C: Char): Boolean;
|
|---|
| 167 | begin
|
|---|
| 168 | Result := (C = ';') or (C = '.');
|
|---|
| 169 | end;
|
|---|
| 170 |
|
|---|
| 171 | function TCompiler.IsWhiteSpace(C: Char): Boolean;
|
|---|
| 172 | begin
|
|---|
| 173 | Result := (C = ' ') or (C = #9) or (C = #10) or (C = #13);
|
|---|
| 174 | end;
|
|---|
| 175 |
|
|---|
| 176 | function TCompiler.ParseBeginEnd(var Block: TBeginEnd): Boolean;
|
|---|
| 177 | var
|
|---|
| 178 | Statement: TStatement;
|
|---|
| 179 | begin
|
|---|
| 180 | Block := nil;
|
|---|
| 181 | Result := False;
|
|---|
| 182 | if CheckNext('begin') then begin
|
|---|
| 183 | Block := TBeginEnd.Create;
|
|---|
| 184 | Expect('begin');
|
|---|
| 185 | while not CheckNext('end') do begin
|
|---|
| 186 | Statement := nil;
|
|---|
| 187 | if not ParseStatement(Statement) then ErrorMsg('');
|
|---|
| 188 | if Assigned(Statement) then
|
|---|
| 189 | Block.Items.Add(Statement);
|
|---|
| 190 | end;
|
|---|
| 191 | Expect('end');
|
|---|
| 192 | Result := True;
|
|---|
| 193 | end;
|
|---|
| 194 | end;
|
|---|
| 195 |
|
|---|
| 196 | function TCompiler.ParseProgram(var Prog: TProgram): Boolean;
|
|---|
| 197 | begin
|
|---|
| 198 | Prog := TProgram.Create;
|
|---|
| 199 | Result := False;
|
|---|
| 200 | if not ParseBeginEnd(Prog.Main) then ErrorMsg('');
|
|---|
| 201 | Expect('.');
|
|---|
| 202 | Result := True;
|
|---|
| 203 | end;
|
|---|
| 204 |
|
|---|
| 205 | function TCompiler.ParseFunctionCall(var FuncCall: TFunctionCall): Boolean;
|
|---|
| 206 | var
|
|---|
| 207 | Token: string;
|
|---|
| 208 | I: Integer;
|
|---|
| 209 | begin
|
|---|
| 210 | FuncCall := nil;
|
|---|
| 211 | Result := False;
|
|---|
| 212 | Token := ReadNext;
|
|---|
| 213 | I := Functions.IndexOf(Token);
|
|---|
| 214 | if I <> -1 then begin
|
|---|
| 215 | FuncCall := TFunctionCall.Create;
|
|---|
| 216 | FuncCall.Name := Token;
|
|---|
| 217 | Result := True;
|
|---|
| 218 | end else ErrorMsg('Unknown command ' + Token);
|
|---|
| 219 | end;
|
|---|
| 220 |
|
|---|
| 221 | function TCompiler.ParseStatement(var Statement: TStatement): Boolean;
|
|---|
| 222 | begin
|
|---|
| 223 | Statement := nil;
|
|---|
| 224 | Result := False;
|
|---|
| 225 | if ParseBeginEnd(TBeginEnd(Statement)) then begin
|
|---|
| 226 | Expect(';');
|
|---|
| 227 | Result := True;
|
|---|
| 228 | end else if not CheckNext('end') then begin
|
|---|
| 229 | ParseFunctionCall(TFunctionCall(Statement));
|
|---|
| 230 | Expect(';');
|
|---|
| 231 | end;
|
|---|
| 232 | end;
|
|---|
| 233 |
|
|---|
| 234 | function TCompiler.ReadNext: string;
|
|---|
| 235 | var
|
|---|
| 236 | C: Char;
|
|---|
| 237 | begin
|
|---|
| 238 | Result := '';
|
|---|
| 239 | while SourcePos < Length(Source) do begin
|
|---|
| 240 | C := Source[SourcePos];
|
|---|
| 241 | Inc(SourcePos);
|
|---|
| 242 | if (Length(Result) = 0) and IsWhiteSpace(C) then Continue;
|
|---|
| 243 | if IsAlphaNumeric(C) then Result := Result + C
|
|---|
| 244 | else if IsSpecialSymbol(C) then begin
|
|---|
| 245 | if Length(Result) > 0 then begin
|
|---|
| 246 | Dec(SourcePos);
|
|---|
| 247 | Break;
|
|---|
| 248 | end else begin
|
|---|
| 249 | Result := Result + C;
|
|---|
| 250 | Break;
|
|---|
| 251 | end;
|
|---|
| 252 | end else begin
|
|---|
| 253 | Dec(SourcePos);
|
|---|
| 254 | Break;
|
|---|
| 255 | end;
|
|---|
| 256 | end;
|
|---|
| 257 | end;
|
|---|
| 258 |
|
|---|
| 259 | var
|
|---|
| 260 | Compiler: TCompiler;
|
|---|
| 261 | Lines: TStringList;
|
|---|
| 262 | { TBeginEnd }
|
|---|
| 263 |
|
|---|
| 264 | constructor TBeginEnd.Create;
|
|---|
| 265 | begin
|
|---|
| 266 | Items := TObjectList<TStatement>.Create;
|
|---|
| 267 | end;
|
|---|
| 268 |
|
|---|
| 269 | destructor TBeginEnd.Destroy;
|
|---|
| 270 | begin
|
|---|
| 271 | FreeAndNil(Items);
|
|---|
| 272 | inherited;
|
|---|
| 273 | end;
|
|---|
| 274 |
|
|---|
| 275 | { TProgram }
|
|---|
| 276 |
|
|---|
| 277 | procedure TProgram.Clear;
|
|---|
| 278 | begin
|
|---|
| 279 | if Assigned(Main) then FreeAndNil(Main);
|
|---|
| 280 | end;
|
|---|
| 281 |
|
|---|
| 282 | constructor TProgram.Create;
|
|---|
| 283 | begin
|
|---|
| 284 | Main := TBeginEnd.Create;
|
|---|
| 285 | end;
|
|---|
| 286 |
|
|---|
| 287 | destructor TProgram.Destroy;
|
|---|
| 288 | begin
|
|---|
| 289 | FreeAndNil(Main);
|
|---|
| 290 | inherited;
|
|---|
| 291 | end;
|
|---|
| 292 |
|
|---|
| 293 | begin
|
|---|
| 294 | try
|
|---|
| 295 | { TODO -oUser -cConsole Main : Insert code here }
|
|---|
| 296 | Compiler := TCompiler.Create;
|
|---|
| 297 | Lines := TStringList.Create;
|
|---|
| 298 | Lines.LoadFromFile('../../Example.pas');
|
|---|
| 299 | Compiler.Source := Lines.Text;
|
|---|
| 300 | Lines.Free;
|
|---|
| 301 | Compiler.Compile;
|
|---|
| 302 | WriteLn(Compiler.Destination);
|
|---|
| 303 | Lines := TStringList.Create;
|
|---|
| 304 | Lines.Text := Compiler.Destination;
|
|---|
| 305 | Lines.SaveToFile('../../Example.out');
|
|---|
| 306 | Lines.Free;
|
|---|
| 307 | Compiler.Free;
|
|---|
| 308 | except
|
|---|
| 309 | on E: Exception do
|
|---|
| 310 | Writeln(E.ClassName, ': ', E.Message);
|
|---|
| 311 | end;
|
|---|
| 312 | end.
|
|---|