source: compiler/trunk/compiler.dpr

Last change on this file was 15, checked in by chronos, 9 years ago
  • Added: Simple test compiler.
File size: 7.0 KB
Line 
1program compiler;
2
3{$APPTYPE CONSOLE}
4
5{$R *.res}
6
7uses
8 System.SysUtils, System.Classes, System.Generics.Collections;
9
10const
11 NewLine = #13#10;
12
13type
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
68function TCompiler.CheckNext(Text: string): Boolean;
69var
70 P: Integer;
71 Token: string;
72begin
73 P := SourcePos;
74 Token := ReadNext;
75 Result := Token = Text;
76 SourcePos := P;
77end;
78
79procedure TCompiler.Compile;
80begin
81 SourcePos := 1;
82 Destination := '';
83 if Assigned(Prog) then
84 FreeAndNil(Prog);
85 ParseProgram(Prog);
86 GenerateProgram(Prog);
87end;
88
89constructor TCompiler.Create;
90begin
91 Prog := TProgram.Create;
92 Functions := TStringList.Create;
93 Functions.Add('WriteLn');
94end;
95
96destructor TCompiler.Destroy;
97begin
98 FreeAndNil(Prog);
99 FreeAndNil(Functions);
100 inherited;
101end;
102
103procedure TCompiler.Emit(Text: string);
104begin
105 Destination := Destination + Text;
106end;
107
108procedure TCompiler.ErrorMsg(Text: string);
109begin
110 WriteLn('Error: ' + Text);
111end;
112
113function TCompiler.Expect(Text: string): Boolean;
114var
115 Token: string;
116begin
117 Token := ReadNext;
118 if Token <> Text then
119 ErrorMsg('Expected ' + Text + ' but ' + Token + ' found');
120 Result := Token = Text;
121end;
122
123procedure TCompiler.GenerateBeginEnd(BeginEnd: TBeginEnd);
124var
125 I: Integer;
126begin
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');
135end;
136
137procedure TCompiler.GenerateFunctionCall(FuncCall: TFunctionCall);
138begin
139 Emit(GetInd + FuncCall.Name);
140end;
141
142procedure TCompiler.GenerateProgram(Prog: TProgram);
143begin
144 Indentation := 0;
145 GenerateBeginEnd(Prog.Main);
146 Emit('.' + NewLine);
147end;
148
149procedure TCompiler.GenerateStatement(Statement: TStatement);
150begin
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');
154end;
155
156function TCompiler.GetInd: string;
157begin
158 REsult := StringOfChar(' ', Indentation * 2);
159end;
160
161function TCompiler.IsAlphaNumeric(C: Char): Boolean;
162begin
163 Result := (C in ['a'..'z']) or (C in ['A'..'Z']) or (C in ['0'..'9'])
164end;
165
166function TCompiler.IsSpecialSymbol(C: Char): Boolean;
167begin
168 Result := (C = ';') or (C = '.');
169end;
170
171function TCompiler.IsWhiteSpace(C: Char): Boolean;
172begin
173 Result := (C = ' ') or (C = #9) or (C = #10) or (C = #13);
174end;
175
176function TCompiler.ParseBeginEnd(var Block: TBeginEnd): Boolean;
177var
178 Statement: TStatement;
179begin
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;
194end;
195
196function TCompiler.ParseProgram(var Prog: TProgram): Boolean;
197begin
198 Prog := TProgram.Create;
199 Result := False;
200 if not ParseBeginEnd(Prog.Main) then ErrorMsg('');
201 Expect('.');
202 Result := True;
203end;
204
205function TCompiler.ParseFunctionCall(var FuncCall: TFunctionCall): Boolean;
206var
207 Token: string;
208 I: Integer;
209begin
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);
219end;
220
221function TCompiler.ParseStatement(var Statement: TStatement): Boolean;
222begin
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;
232end;
233
234function TCompiler.ReadNext: string;
235var
236 C: Char;
237begin
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;
257end;
258
259var
260 Compiler: TCompiler;
261 Lines: TStringList;
262{ TBeginEnd }
263
264constructor TBeginEnd.Create;
265begin
266 Items := TObjectList<TStatement>.Create;
267end;
268
269destructor TBeginEnd.Destroy;
270begin
271 FreeAndNil(Items);
272 inherited;
273end;
274
275{ TProgram }
276
277procedure TProgram.Clear;
278begin
279 if Assigned(Main) then FreeAndNil(Main);
280end;
281
282constructor TProgram.Create;
283begin
284 Main := TBeginEnd.Create;
285end;
286
287destructor TProgram.Destroy;
288begin
289 FreeAndNil(Main);
290 inherited;
291end;
292
293begin
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;
312end.
Note: See TracBrowser for help on using the repository browser.