source: branches/easy compiler/UCompiler.pas

Last change on this file was 149, checked in by chronos, 7 years ago
  • Fixed: Now arrays of string and integer are supported and executed correctly by executor.
  • Added: IfNotEqual command.
File size: 16.1 KB
Line 
1unit UCompiler;
2
3{$mode delphi}{$H+}
4
5interface
6
7uses
8 Classes, SysUtils, USourceCode, UTargetCode;
9
10type
11 TSourceTokenKind = (stNone, stString, stIdentifier, stEof, stInteger,
12 stSpecialSymbol);
13 TSourceToken = record
14 Text: string;
15 Kind: TSourceTokenKind;
16 end;
17
18 { TSourceTokens }
19
20 TSourceTokens = class
21 Tokens: array of TSourceToken;
22 procedure AddToken(Kind: TSourceTokenKind; Text: string);
23 end;
24
25 { TTokenizer }
26
27 TTokenizer = class
28 TokenIndex: Integer;
29 Tokens: TSourceTokens;
30 function IsAlpha(Character: Char): Boolean;
31 function IsDigit(Character: Char): Boolean;
32 function IsAlphaNumeric(Character: Char): Boolean;
33 function IsWhiteSpace(Character: Char): Boolean;
34 function IsKeyword(Text: string): Boolean;
35 function GetNext(Kind: TSourceTokenKind = stNone): TSourceToken;
36 function CheckNext(Text: string; Kind: TSourceTokenKind): Boolean;
37 procedure Tokenize(Text: string; Tokens: TSourceTokens);
38 end;
39
40 { TCompiler }
41
42 TCompiler = class
43 private
44 Tokenizer: TTokenizer;
45 procedure Parse(Code: TSourceCode);
46 function ParseBeginEnd(SourceCode: TSourceCode; out BeginEnd: TCommandBeginEnd): Boolean;
47 function ParseBreak(SourceCode: TSourceCode; out CommandBreak: TCommandBreak
48 ): Boolean;
49 function ParseCommand(SourceCode: TSourceCode; out Command: TSourceCommand
50 ): Boolean;
51 function ParseFunctionCall(SourceCode: TSourceCode; out FunctionCall: TCommandFunctionCall): Boolean;
52 function ParseIfEqual(SourceCode: TSourceCode; out IfEqual: TCommandIfEqual): Boolean;
53 function ParseIfNotEqual(SourceCode: TSourceCode; out IfNotEqual: TCommandIfNotEqual): Boolean;
54 function ParseReference(SourceCode: TSourceCode): TSourceReference;
55 function ParseReferenceVariable(SourceCode: TSourceCode): TSourceReference;
56 function ParseRepeat(SourceCode: TSourceCode; out
57 CommandRepeat: TCommandRepeat): Boolean;
58 function ParseVar(SourceCode: TSourceCode): Boolean;
59 public
60 procedure Compile(Source: string; SourceCode: TSourceCode);
61 procedure Convert(SourceCode: TSourceCode; Target: TTargetCode);
62 end;
63
64
65implementation
66
67{ TSourceTokens }
68
69procedure TSourceTokens.AddToken(Kind: TSourceTokenKind; Text: string);
70begin
71 SetLength(Tokens, Length(Tokens) + 1);
72 Tokens[Length(Tokens) - 1].Kind := Kind;
73 Tokens[Length(Tokens) - 1].Text := Text;
74end;
75
76{ TTokenizer }
77
78function TTokenizer.IsAlpha(Character: Char): Boolean;
79begin
80 Result := (Character in ['a'..'z']) or (Character in ['A'..'Z']);
81end;
82
83function TTokenizer.IsDigit(Character: Char): Boolean;
84begin
85 Result := Character in ['0'..'9'];
86end;
87
88function TTokenizer.IsAlphaNumeric(Character: Char): Boolean;
89begin
90 Result := IsAlpha(Character) or IsDigit(Character);
91end;
92
93function TTokenizer.IsWhiteSpace(Character: Char): Boolean;
94begin
95 Result := (Character = ' ') or (Character = #13) or (Character = #10) or
96 (Character = #9);
97end;
98
99function TTokenizer.IsKeyword(Text: string): Boolean;
100begin
101 Result := False;
102end;
103
104function TTokenizer.GetNext(Kind: TSourceTokenKind = stNone): TSourceToken;
105begin
106 if TokenIndex < Length(Tokens.Tokens) then begin
107 Result := Tokens.Tokens[TokenIndex];
108 Inc(TokenIndex);
109 end else begin
110 Result.Kind := stEof;
111 Result.Text := '';
112 end;
113 if (Kind <> stNone) and (Result.Kind <> Kind) then
114 raise Exception.Create('Unexpected token type');
115end;
116
117function TTokenizer.CheckNext(Text: string; Kind: TSourceTokenKind): Boolean;
118var
119 Index: Integer;
120 Token: TSourceToken;
121begin
122 Index := TokenIndex;
123 Token := GetNext;
124 Result := (LowerCase(Token.Text) = Text) and (Token.Kind = Kind);
125 TokenIndex := Index;
126end;
127
128procedure TTokenizer.Tokenize(Text: string; Tokens: TSourceTokens);
129type
130 TTokenizerState = (tsNone, tsIdentifier, tsString, tsInteger, tsSpecialSymbol);
131var
132 State: TTokenizerState;
133 Token: string;
134 I: Integer;
135begin
136 Self.Tokens := Tokens;
137 TokenIndex := 0;
138 I := 1;
139 State := tsNone;
140 while I < Length(Text) do begin
141 if State = tsNone then begin
142 if IsDigit(Text[I]) then begin
143 State := tsInteger;
144 Token := Text[I];
145 end else
146 if IsAlpha(Text[I]) then begin
147 State := tsIdentifier;
148 Token := Text[I];
149 end else
150 if IsWhiteSpace(Text[I]) then begin
151 // Do nothing
152 end else
153 if Text[I] = '''' then begin
154 State := tsString;
155 Token := '';
156 end else
157 if (Text[I] = '[') or (Text[I] = ']') then begin
158 Tokens.AddToken(stSpecialSymbol, Text[I]);
159 end else
160 raise Exception.Create('Unexpected character: ' + Text[I]);
161 end else
162 if State = tsIdentifier then begin
163 if IsAlphaNumeric(Text[I]) then begin
164 Token := Token + Text[I];
165 end else begin
166 Tokens.AddToken(stIdentifier, Token);
167 State := tsNone;
168 Dec(I);
169 end;
170 end else
171 if State = tsInteger then begin
172 if IsDigit(Text[I]) then begin
173 Token := Token + Text[I];
174 end else begin
175 Tokens.AddToken(stInteger, Token);
176 State := tsNone;
177 Dec(I);
178 end;
179 end else
180 if State = tsString then begin
181 if Text[I] <> '''' then begin
182 Token := Token + Text[I];
183 end else begin
184 Tokens.AddToken(stString, Token);
185 State := tsNone;
186 end;
187 end else
188 raise Exception.Create('Unsupported tokenizer state');
189 Inc(I);
190 end;
191end;
192
193{ TCompiler }
194
195procedure TCompiler.Parse(Code: TSourceCode);
196begin
197 Tokenizer.TokenIndex := 0;
198 ParseBeginEnd(Code, Code.Main);
199end;
200
201function TCompiler.ParseVar(SourceCode: TSourceCode): Boolean;
202var
203 Token: TSourceToken;
204 Token2: TSourceToken;
205 Variable: TSourceVariable;
206 ValueType: TSourceType;
207 TokenIndex: Integer;
208begin
209 Result := False;
210 TokenIndex := Tokenizer.TokenIndex;
211 Token := Tokenizer.GetNext;
212 if LowerCase(Token.Text) = 'var' then begin
213 Token := Tokenizer.GetNext;
214 Token2 := Tokenizer.GetNext;
215 if Token2.Kind <> stIdentifier then
216 raise Exception.Create('Expected type parameter');
217 Variable := SourceCode.Variables.Search(Token.Text);
218 if not Assigned(Variable) then begin
219 ValueType := SourceCode.Types.Search(Token2.Text);
220 if not Assigned(ValueType) then
221 raise Exception.Create('Unsupported type: ' + Token2.Text);
222 Variable := SourceCode.Variables.AddNew(Token.Text,
223 ValueType);
224 end else raise Exception.Create('Variable redefined: ' + Token.Text);
225 Result := True;
226 end;
227 if not Result then Tokenizer.TokenIndex := TokenIndex;
228end;
229
230function TCompiler.ParseReference(SourceCode: TSourceCode): TSourceReference;
231var
232 Token: TSourceToken;
233 Token2: TSourceToken;
234 NewReference: TSourceReference;
235begin
236 Token := Tokenizer.GetNext;
237 if Token.Kind = stString then begin
238 NewReference := TSourceReferenceConstant.Create;
239 TSourceReferenceConstant(NewReference).Constant :=
240 SourceCode.Constants.AddNewString(Token.Text);
241 end else
242 if Token.Kind = stIdentifier then begin
243 if Tokenizer.CheckNext('[', stSpecialSymbol) then begin
244 Token2 := Tokenizer.GetNext;
245 NewReference := TSourceReferenceArray.Create;
246 TSourceReferenceArray(NewReference).ArrayRef := SourceCode.Variables.Search(Token.Text);
247 if TSourceReferenceArray(NewReference).ArrayRef = nil then
248 raise Exception.Create('Variable not found: ' + Token.Text);
249 TSourceReferenceArray(NewReference).Index := ParseReference(SourceCode);
250
251 Token2 := Tokenizer.GetNext;
252 if (Token2.Text <> ']') or (Token2.Kind <> stSpecialSymbol) then
253 raise Exception.Create('Expected ]');
254 end else begin
255 NewReference := TSourceReferenceVariable.Create;
256 TSourceReferenceVariable(NewReference).Variable :=
257 SourceCode.Variables.Search(Token.Text);
258 if TSourceReferenceVariable(NewReference).Variable = nil then
259 raise Exception.Create('Variable not found: ' + Token.Text);
260 end;
261 end else
262 if Token.Kind = stInteger then begin
263 NewReference := TSourceReferenceConstant.Create;
264 TSourceReferenceConstant(NewReference).Constant :=
265 SourceCode.Constants.AddNewInteger(StrToInt(Token.Text));
266 end else
267 raise Exception.Create('Unexpected parameter');
268 Result := NewReference;
269end;
270
271function TCompiler.ParseReferenceVariable(SourceCode: TSourceCode): TSourceReference;
272var
273 Token: TSourceToken;
274 Token2: TSourceToken;
275 NewReference: TSourceReference;
276begin
277 Token := Tokenizer.GetNext;
278 if Token.Kind = stIdentifier then begin
279 if Tokenizer.CheckNext('[', stSpecialSymbol) then begin
280 Token2 := Tokenizer.GetNext;
281 NewReference := TSourceReferenceArray.Create;
282 TSourceReferenceArray(NewReference).ArrayRef := SourceCode.Variables.Search(Token.Text);
283 if TSourceReferenceArray(NewReference).ArrayRef = nil then
284 raise Exception.Create('Variable not found: ' + Token.Text);
285 TSourceReferenceArray(NewReference).Index := ParseReference(SourceCode);
286
287 Token2 := Tokenizer.GetNext;
288 if (Token2.Text <> ']') or (Token2.Kind <> stSpecialSymbol) then
289 raise Exception.Create('Expected ]');
290 end else begin
291 NewReference := TSourceReferenceVariable.Create;
292 TSourceReferenceVariable(NewReference).Variable :=
293 SourceCode.Variables.Search(Token.Text);
294 if TSourceReferenceVariable(NewReference).Variable = nil then
295 raise Exception.Create('Variable not found: ' + Token.Text);
296 end;
297 end else raise Exception.Create('Unexpected parameter');
298 Result := NewReference;
299end;
300
301function TCompiler.ParseFunctionCall(SourceCode: TSourceCode; out FunctionCall: TCommandFunctionCall): Boolean;
302var
303 Funct: TSourceFunction;
304 Param: TSourceFunctionParameter;
305 Token: TSourceToken;
306 TokenIndex: Integer;
307 Keyword: string;
308begin
309 Result := False;
310 TokenIndex := Tokenizer.TokenIndex;
311 Token := Tokenizer.GetNext;
312 Keyword := LowerCase(Token.Text);
313 Funct := SourceCode.Functions.Search(Keyword);
314 if Assigned(Funct) then begin
315 FunctionCall := TCommandFunctionCall.Create;
316 FunctionCall.Name := Keyword;
317 for Param in Funct.Parameters do
318 if Param.Kind = pkString then begin
319 FunctionCall.Parameters.Add(ParseReference(SourceCode))
320 end else
321 if Param.Kind = pkVariable then begin
322 FunctionCall.Parameters.Add(ParseReferenceVariable(SourceCode));
323 end else
324 raise Exception.Create('Unsupported parameter type.');
325 Result := True;
326 end;
327 if not Result then Tokenizer.TokenIndex := TokenIndex;
328end;
329
330function TCompiler.ParseIfEqual(SourceCode: TSourceCode; out IfEqual: TCommandIfEqual): Boolean;
331var
332 Token: TSourceToken;
333 TokenIndex: Integer;
334 Keyword: string;
335begin
336 Result := False;
337 TokenIndex := Tokenizer.TokenIndex;
338 Token := Tokenizer.GetNext;
339 Keyword := LowerCase(Token.Text);
340 if Keyword = 'ifequal' then begin
341 IfEqual := TCommandIfEqual.Create;
342 IfEqual.Reference1 := ParseReference(SourceCode);
343 IfEqual.Reference2 := ParseReference(SourceCode);
344 Result := True;
345 end;
346 if not Result then Tokenizer.TokenIndex := TokenIndex;
347end;
348
349function TCompiler.ParseIfNotEqual(SourceCode: TSourceCode; out
350 IfNotEqual: TCommandIfNotEqual): Boolean;
351var
352 Token: TSourceToken;
353 TokenIndex: Integer;
354 Keyword: string;
355begin
356 Result := False;
357 TokenIndex := Tokenizer.TokenIndex;
358 Token := Tokenizer.GetNext;
359 Keyword := LowerCase(Token.Text);
360 if Keyword = 'ifnotequal' then begin
361 IfNotEqual := TCommandIfNotEqual.Create;
362 IfNotEqual.Reference1 := ParseReference(SourceCode);
363 IfNotEqual.Reference2 := ParseReference(SourceCode);
364 Result := True;
365 end;
366 if not Result then Tokenizer.TokenIndex := TokenIndex;
367end;
368
369function TCompiler.ParseBreak(SourceCode: TSourceCode; out CommandBreak: TCommandBreak): Boolean;
370var
371 Token: TSourceToken;
372 TokenIndex: Integer;
373 Keyword: string;
374begin
375 Result := False;
376 TokenIndex := Tokenizer.TokenIndex;
377 Token := Tokenizer.GetNext;
378 Keyword := LowerCase(Token.Text);
379 if Keyword = 'break' then begin
380 CommandBreak := TCommandBreak.Create;
381 Result := True;
382 end;
383 if not Result then Tokenizer.TokenIndex := TokenIndex;
384end;
385
386function TCompiler.ParseRepeat(SourceCode: TSourceCode; out CommandRepeat: TCommandRepeat): Boolean;
387var
388 Token: TSourceToken;
389 TokenIndex: Integer;
390 Keyword: string;
391 Command: TSourceCommand;
392begin
393 Result := False;
394 TokenIndex := Tokenizer.TokenIndex;
395 Token := Tokenizer.GetNext;
396 Keyword := LowerCase(Token.Text);
397 if Keyword = 'repeat' then begin
398 CommandRepeat := TCommandRepeat.Create;
399 if ParseCommand(SourceCode, Command) then begin
400 CommandRepeat := TCommandRepeat.Create;
401 CommandRepeat.Command := Command;
402 Command.Parent := CommandRepeat;
403 end else
404 raise Exception.Create('Unexpected token');
405 Result := True;
406 end;
407 if not Result then Tokenizer.TokenIndex := TokenIndex;
408end;
409
410function TCompiler.ParseCommand(SourceCode: TSourceCode; out Command: TSourceCommand): Boolean;
411var
412 CommandBeginEnd: TCommandBeginEnd;
413 CommandIfEqual: TCommandIfEqual;
414 CommandFunctionCall: TCommandFunctionCall;
415 CommandBreak: TCommandBreak;
416 CommandRepeat: TCommandRepeat;
417 CommandIfNotEqual: TCommandIfNotEqual;
418begin
419 Command := nil;
420 Result := True;
421 if ParseVar(SourceCode) then else
422 if ParseBeginEnd(SourceCode, CommandBeginEnd) then begin
423 Command := CommandBeginEnd;
424 end else
425 if ParseIfEqual(SourceCode, CommandIfEqual) then begin
426 Command := CommandIfEqual;
427 end else
428 if ParseIfNotEqual(SourceCode, CommandIfNotEqual) then begin
429 Command := CommandIfNotEqual;
430 end else
431 if ParseBreak(SourceCode, CommandBreak) then begin
432 Command := CommandBreak;
433 end else
434 if ParseRepeat(SourceCode, CommandRepeat) then begin
435 Command := CommandRepeat;
436 end else
437 if ParseFunctionCall(SourceCode, CommandFunctionCall) then begin
438 Command := CommandFunctionCall;
439 end else Result := False;
440end;
441
442function TCompiler.ParseBeginEnd(SourceCode: TSourceCode; out BeginEnd: TCommandBeginEnd): Boolean;
443var
444 Token: TSourceToken;
445 Keyword: string;
446 TokenIndex: Integer;
447 Command: TSourceCommand;
448begin
449 Result := False;
450 TokenIndex := Tokenizer.TokenIndex;
451 Token := Tokenizer.GetNext;
452 Keyword := LowerCase(Token.Text);
453 if Keyword = 'begin' then begin
454 BeginEnd := TCommandBeginEnd.Create;
455 BeginEnd.SourceCode := SourceCode;
456 while True do begin
457 if ParseCommand(SourceCode, Command) then begin
458 if Assigned(Command) then begin
459 BeginEnd.Commands.Add(Command);
460 Command.Parent := BeginEnd;
461 end;
462 end else
463 if Tokenizer.CheckNext('end', stIdentifier) then begin
464 Break;
465 end else begin
466 Token := Tokenizer.GetNext;
467 Keyword := LowerCase(Token.Text);
468 raise Exception.Create('Unknown token: ' + Keyword);
469 end;
470 end;
471 Token := Tokenizer.GetNext;
472 if LowerCase(Token.Text) <> 'end' then
473 raise Exception.Create('Expected end keyword');
474 Result := True;
475 end;
476 if not Result then Tokenizer.TokenIndex := TokenIndex;
477end;
478
479procedure TCompiler.Compile(Source: string; SourceCode: TSourceCode);
480var
481 SourceTokens: TSourceTokens;
482begin
483 SourceTokens := TSourceTokens.Create;
484 Tokenizer := TTokenizer.Create;
485
486 Tokenizer.Tokenize(Source, SourceTokens);
487 Parse(SourceCode);
488
489 Tokenizer.Free;
490 SourceTokens.Free;
491end;
492
493procedure TCompiler.Convert(SourceCode: TSourceCode; Target: TTargetCode);
494var
495 I: Integer;
496 Instruction: TSourceCommand;
497 TargetInstruction: TTargetInstruction;
498begin
499{ Target.Instructions.Count := 0;
500 for I := 0 to SourceCode.Instructions.Count - 1 do begin
501 Instruction := TSourceCommand(SourceCode.Instructions[I]);
502 if Instruction is TCommandFunctionCall then begin
503 TargetInstruction := TTargetInstruction.Create;
504 TargetInstruction.Kind := tiFunction;
505 TargetInstruction.Name := TCommandFunctionCall(Instruction).Name;
506 Target.Instructions.Add(TargetInstruction)
507 end else raise Exception.Create('Unsupported source instruction');
508 end;
509 }
510end;
511
512end.
513
Note: See TracBrowser for help on using the repository browser.