123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 |
- {
- This file is part of the Free Component Library (FCL)
- Copyright (c) 2025 by Michael Van Canneyt
- Pascal syntax highlighter
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- **********************************************************************}
- {$MODE objfpc}
- {$H+}
- unit syntax.pascal;
- interface
- uses
- types, sysutils, syntax.highlighter;
- type
- { TPascalSyntaxHighlighter }
- TPascalSyntaxHighlighter = class(TSyntaxHighlighter)
- private
- FSource: string;
- FPos: integer;
- protected
- procedure CheckCategories;
- procedure ProcessComment1(var endPos: integer; akind : TSyntaxHighlightKind);
- procedure ProcessComment2(var endPos: integer);
- function CheckForComment(var endPos: integer): boolean;
- procedure ProcessAsm(var endPos: integer);
- function CheckForKeyword(var endPos: integer): boolean;
- procedure ProcessSymbol(var endPos: integer);
- class function GetLanguages: TStringDynArray; override;
- public
- constructor Create; override;
- class var
- CategoryPascal,
- CategoryIdentifier : Integer;
- function Execute(const Source: string): TSyntaxTokenArray; override;
- end;
- function DoPascalHighlighting(const Source: string): TSyntaxTokenArray;
- implementation
- const
- MaxKeywordLength = 15;
- MaxKeyword = 60;
- KeywordTable: array[0..MaxKeyword] of string =
- ('AND', 'ARRAY', 'ASM', 'ASSEMBLER',
- 'BEGIN', 'BREAK',
- 'CASE', 'CONST', 'CONSTRUCTOR', 'CLASS',
- 'DEFAULT', 'DESTRUCTOR', 'DIV', 'DO', 'DOWNTO',
- 'ELSE', 'END', 'EXCEPT', 'EXIT',
- 'FINALIZATION', 'FINALLY', 'FOR', 'FUNCTION',
- 'GOTO',
- 'IF', 'IMPLEMENTATION', 'IN', 'INHERITED', 'INITIALIZATION', 'INTERFACE',
- 'NIL', 'NOT',
- 'OBJECT', 'OF', 'ON', 'OR', 'OVERRIDE',
- 'PACKED', 'PRIVATE', 'PROCEDURE', 'PROGRAM', 'PROPERTY', 'PROTECTED',
- 'PUBLIC', 'PUBLISHED',
- 'RAISE', 'RECORD', 'REPEAT', 'RESOURCESTRING',
- 'SET',
- 'THEN', 'TRY', 'TYPE',
- 'UNIT', 'UNTIL', 'USES',
- 'VAR', 'VIRTUAL',
- 'WHILE', 'WITH',
- 'XOR');
- KeywordAsmIndex = 2;
- { TPascalSyntaxHighlighter }
- procedure TPascalSyntaxHighlighter.CheckCategories;
- begin
- if CategoryPascal=0 then
- begin
- CategoryPascal:=RegisterCategory('pascal');
- CategoryIdentifier:=RegisterCategory('identifier');
- end;
- end;
- procedure TPascalSyntaxHighlighter.ProcessComment1(var endPos: integer; akind: TSyntaxHighlightKind);
- var
- startPos: integer;
- begin
- startPos := FPos;
- Inc(FPos); // Skip the opening '{'
- while (FPos <= Length(FSource)) and (FSource[FPos] <> '}') do
- Inc(FPos);
- if (FPos <= Length(FSource)) and (FSource[FPos] = '}') then
- Inc(FPos);
- endPos := FPos - 1;
- AddToken(Copy(FSource, startPos, endPos - startPos + 1), aKind);
- end;
- procedure TPascalSyntaxHighlighter.ProcessComment2(var endPos: integer);
- var
- startPos: integer;
- begin
- startPos := FPos;
- Inc(FPos, 2); // Skip the opening '(*'
- while (FPos < Length(FSource)) and not ((FSource[FPos] = '*') and (FSource[FPos + 1] = ')')) do
- Inc(FPos);
- if (FPos < Length(FSource)) and (FSource[FPos] = '*') and (FSource[FPos + 1] = ')') then
- begin
- Inc(FPos, 2);
- end
- else
- FPos := Length(FSource) + 1;
- endPos := FPos - 1;
- AddToken(Copy(FSource, startPos, endPos - startPos + 1), shComment);
- end;
- function TPascalSyntaxHighlighter.CheckForComment(var endPos: integer): boolean;
- var
- startPos: integer;
- kind: TSyntaxHighlightKind;
- begin
- Result := True;
- startPos := FPos;
- if (FPos <= Length(FSource)) and (FSource[FPos] = '{') then
- begin
- if (FPos < Length(FSource)) and (FSource[FPos + 1] = '$') then
- kind := shDirective
- else
- kind := shComment;
- ProcessComment1(endPos,kind);
- end
- else if (FPos < Length(FSource)) and (FSource[FPos] = '(') and (FSource[FPos + 1] = '*') then
- begin
- ProcessComment2(endPos);
- end
- else if (FPos < Length(FSource)) and (FSource[FPos] = '/') and (FSource[FPos + 1] = '/') then
- begin
- while (FPos <= Length(FSource)) and (FSource[FPos] <> #10) and (FSource[FPos] <> #13) do
- Inc(FPos);
- endPos := FPos - 1;
- AddToken(Copy(FSource, startPos, endPos - startPos + 1), shComment);
- end
- else
- Result := False;
- end;
- procedure TPascalSyntaxHighlighter.ProcessAsm(var endPos: integer);
- var
- startPos: integer;
- lastChar: char;
- begin
- startPos := FPos;
- lastChar := ' ';
- while FPos <= Length(FSource) do
- begin
- if (lastChar in [' ', #9, #10, #13]) and
- (FPos + 2 <= Length(FSource)) and
- (UpCase(FSource[FPos]) = 'E') and (UpCase(FSource[FPos + 1]) = 'N') and
- (UpCase(FSource[FPos + 2]) = 'D') then
- begin
- endPos := FPos - 1;
- if endPos >= startPos then
- AddToken(Copy(FSource, startPos, endPos - startPos + 1), shAssembler);
- AddToken('END', shKeyword);
- Inc(FPos, 3);
- Exit;
- end
- else
- begin
- if CheckForComment(endPos) then
- lastChar := ' '
- else
- begin
- lastChar := FSource[FPos];
- Inc(FPos);
- end;
- end;
- end;
- endPos := FPos - 1;
- if endPos >= startPos then
- AddToken(Copy(FSource, startPos, endPos - startPos + 1), shAssembler);
- end;
- function TPascalSyntaxHighlighter.CheckForKeyword(var endPos: integer): boolean;
- const
- IdentifierChars = ['0'..'9', 'A'..'Z', 'a'..'z', '_'];
- var
- i, lIdx: integer;
- keyword, ukeyword: string;
- begin
- i := 0;
- while (FPos + i <= Length(FSource))
- and (i < MaxKeywordLength)
- and (FSource[FPos + i] in IdentifierChars) do
- Inc(i);
- keyword := Copy(FSource, FPos, i);
- ukeyword := UpperCase(keyword);
- Result := False;
- lIdx:=MaxKeyWord;
- While (Not Result) and (lIdx>=0) do
- begin
- Result:=KeywordTable[lIdx] = ukeyword;
- Dec(lIdx);
- end;
- if not Result then
- Exit;
- Inc(lIdx); // Index of actual keyword
- Inc(FPos,i);
- endPos:=FPos - 1;
- AddToken(keyword,shKeyword);
- if lIdx=KeywordAsmIndex then
- ProcessAsm(endPos);
- end;
- procedure TPascalSyntaxHighlighter.ProcessSymbol(var endPos: integer);
- var
- startPos: integer;
- begin
- startPos := FPos;
- if (FPos < Length(FSource)) and (FSource[FPos] = ':') and (FSource[FPos + 1] = '=') then
- Inc(FPos, 2)
- else
- Inc(FPos);
- endPos := FPos - 1;
- AddToken(Copy(FSource, startPos, endPos - startPos + 1), shSymbol);
- end;
- class function TPascalSyntaxHighlighter.GetLanguages: TStringDynArray;
- begin
- Result:=['pascal','delphi','objectpascal']
- end;
- constructor TPascalSyntaxHighlighter.Create;
- begin
- inherited Create;
- CheckCategories;
- DefaultCategory:=CategoryPascal;
- end;
- function TPascalSyntaxHighlighter.Execute(const Source: string): TSyntaxTokenArray;
- var
- endPos: integer;
- StringLength: integer;
- lLen,startPos: integer;
- ch: char;
- begin
- Result:=Nil;
- CheckCategories;
- if Length(Source) = 0 then
- Exit;
- FSource:=Source;
- lLen:=Length(FSource);
- FTokens.Reset;
- FPos := 1;
- EndPos:=0;
- while FPos <= llen do
- begin
- ch := FSource[FPos];
- if CheckForComment(endPos) then
- begin
- FPos := endPos + 1;
- continue;
- end;
- case ch of
- ',', ';', ':', '.', '(', ')', '[', ']', '<', '>', '=',
- '*', '/', '+', '-', '^', '&', '@':
- ProcessSymbol(endPos);
- '#':
- begin
- startPos := FPos;
- Inc(FPos);
- if (FPos <= Length(FSource)) and (FSource[FPos] = '$') then
- Inc(FPos);
- while (FPos <= Length(FSource)) and (FSource[FPos] >= '0') and (FSource[FPos] <= '9') do
- Inc(FPos);
- endPos := FPos - 1;
- AddToken(Copy(FSource, startPos, endPos - startPos + 1), shCharacters);
- end;
- '$':
- begin
- startPos := FPos;
- Inc(FPos);
- while (FPos <= Length(FSource)) and (FSource[FPos] in ['0'..'9', 'A'..'F', 'a'..'f']) do
- Inc(FPos);
- endPos := FPos - 1;
- AddToken(Copy(FSource, startPos, endPos - startPos + 1), shNumbers);
- end;
- '0'..'9':
- begin
- startPos := FPos;
- Inc(FPos);
- while (FPos <= Length(FSource)) and (FSource[FPos] >= '0') and (FSource[FPos] <= '9') do
- Inc(FPos);
- endPos := FPos - 1;
- AddToken(Copy(FSource, startPos, endPos - startPos + 1), shNumbers);
- end;
- '''':
- begin
- startPos := FPos;
- Inc(FPos);
- StringLength := 0;
- while (FPos <= Length(FSource)) do
- begin
- if FSource[FPos] = '''' then
- if (FPos < Length(FSource)) and (FSource[FPos + 1] = '''') then
- begin
- Inc(FPos, 2);
- Inc(StringLength);
- end
- else
- begin
- Inc(FPos);
- break;
- end
- else
- begin
- Inc(StringLength);
- Inc(FPos);
- end;
- end;
- endPos := FPos - 1;
- if StringLength = 1 then
- AddToken(Copy(FSource, startPos, endPos - startPos + 1), shCharacters)
- else if (FPos > Length(FSource)) and (FSource[endPos] <> '''') then
- AddToken(Copy(FSource, startPos, endPos - startPos + 1), shInvalid)
- else
- AddToken(Copy(FSource, startPos, endPos - startPos + 1), shStrings);
- end;
- '_', 'A'..'Z', 'a'..'z':
- begin
- if not CheckForKeyword(endPos) then
- begin
- startPos := FPos;
- while (FPos <= Length(FSource)) and (FSource[FPos] in ['0'..'9', '_', 'A'..'Z', 'a'..'z']) do
- Inc(FPos);
- endPos := FPos - 1;
- AddToken(Copy(FSource, startPos, endPos - startPos + 1), shDefault);
- end;
- end;
- ' ', #9, #10, #13:
- begin
- startPos := FPos;
- while (FPos <= Length(FSource)) and (FSource[FPos] in [' ', #9, #10, #13]) do
- Inc(FPos);
- endPos := FPos - 1;
- AddToken(Copy(FSource, startPos, endPos - startPos + 1), shDefault);
- end;
- else
- AddToken(ch, shInvalid);
- Inc(FPos);
- end;
- if FPos = endPos then
- Inc(FPos);
- end;
- Result := FTokens.GetTokens;
- end;
- function DoPascalHighlighting(const Source: string): TSyntaxTokenArray;
- var
- highlighter: TPascalSyntaxHighlighter;
- begin
- highlighter := TPascalSyntaxHighlighter.Create;
- try
- Result := highlighter.Execute(Source);
- finally
- highlighter.Free;
- end;
- end;
- initialization
- TPascalSyntaxHighlighter.Register;
- end.
|