1 {-------------------------------------------------------------------------------
2 The contents of this file are subject to the Mozilla Public License
3 Version 1.1 (the "License"); you may not use this file except in compliance
4 with the License. You may obtain a copy of the License at
5 http://www.mozilla.org/MPL/
6 
7 Software distributed under the License is distributed on an "AS IS" basis,
8 WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
9 the specific language governing rights and limitations under the License.
10 
11 The Original Code is: SynHighlighterPython.pas, released 2000-06-23.
12 The Original Code is based on the odPySyn.pas file from the
13 mwEdit component suite by Martin Waldenburg and other developers, the Initial
14 Author of this file is Olivier Deckmyn.
15 Portions created by M.Utku Karatas and Dennis Chuah.
16 All Rights Reserved.
17 
18 Contributors to the SynEdit and mwEdit projects are listed in the
19 Contributors.txt file.
20 
21 Alternatively, the contents of this file may be used under the terms of the
22 GNU General Public License Version 2 or later (the "GPL"), in which case
23 the provisions of the GPL are applicable instead of those above.
24 If you wish to allow use of your version of this file only under the terms
25 of the GPL and not to allow others to use your version of this file
26 under the MPL, indicate your decision by deleting the provisions above and
27 replace them with the notice and other provisions required by the GPL.
28 If you do not delete the provisions above, a recipient may use your version
29 of this file under either the MPL or the GPL.
30 
31 $Id: synhighlighterpython.pas 52753 2016-07-28 07:56:24Z mattias $
32 
33 You may retrieve the latest version of this file at the SynEdit home page,
34 located at http://SynEdit.SourceForge.net
35 
36 Known Issues:
37 -------------------------------------------------------------------------------}
38 {
39 @abstract(A Python language highlighter for SynEdit)
40 @author(Olivier Deckmyn, converted to SynEdit by David Muir <dhmn@dmsoftware.co.uk>)
41 @created(unknown, converted to SynEdit on 2000-06-23)
42 @lastmod(2003-02-13)
43 The SynHighlighterPython implements a highlighter for Python for the SynEdit projects.
44 }
45 unit SynHighlighterPython;
46 
47 {$I SynEdit.inc}
48 
49 interface
50 
51 uses
52   IniFiles, //THashedStringList
53   LCLIntf, LCLType,
54   SynEditHighlighter, SynEditTypes,
55   Graphics, SysUtils, Classes;
56 
57 const
58   ALPHA_CHARS = ['_', 'a'..'z', 'A'..'Z'];
59   IDENTIFIER_CHARS = ['0'..'9'] + ALPHA_CHARS;
60 
61 type
62   TtkTokenKind = (tkComment, tkIdentifier, tkKey, tkNull, tkNumber, tkSpace,
63     tkString, tkSymbol, tkNonKeyword, tkTripleQuotedString,
64     tkSystemDefined, tkHex, tkOct, tkFloat, tkUnknown);
65 
66   TRangeState = (rsANil, rsComment, rsUnKnown, rsMultilineString, rsMultilineString2,
67                  rsMultilineString3 //this is to indicate if a string is made multiline by backslash char at line end (as in C++ highlighter)
68                 );
69 
70   TProcTableProc = procedure of object;
71 
72 type
73   TSynPythonSyn = class(TSynCustomHighLighter)
74   private
75     fStringStarter: char;  // used only for rsMultilineString3 stuff
76     fRange: TRangeState;
77     fLine: PChar;
78     fLineNumber: Integer;
79     fProcTable: array[#0..#255] of TProcTableProc;
80     fToIdent: PChar;
81     fTokenPos: Integer;
82     FTokenID: TtkTokenKind;
83     FKeywords: TStringList;
84 
85     fStringAttri: TSynHighlighterAttributes;
86     fDocStringAttri: TSynHighlighterAttributes;
87     fNumberAttri: TSynHighlighterAttributes;
88     fHexAttri: TSynHighlighterAttributes;
89     fOctalAttri: TSynHighlighterAttributes;
90     fFloatAttri: TSynHighlighterAttributes;
91     fKeyAttri: TSynHighlighterAttributes;
92     fNonKeyAttri: TSynHighlighterAttributes;
93     fSystemAttri: TSynHighlighterAttributes;
94     fSymbolAttri: TSynHighlighterAttributes;
95     fCommentAttri: TSynHighlighterAttributes;
96     fIdentifierAttri: TSynHighlighterAttributes;
97     fSpaceAttri: TSynHighlighterAttributes;
98     fErrorAttri: TSynHighlighterAttributes;
99 
100     procedure SymbolProc;
101     procedure CRProc;
102     procedure CommentProc;
103     procedure GreaterProc;
104     procedure IdentProc;
105     procedure LFProc;
106     procedure LowerProc;
107     procedure NullProc;
108     procedure NumberProc;
109     procedure SpaceProc;
110     procedure PreStringProc;
111     procedure UnicodeStringProc;
112     procedure StringProc;
113     procedure String2Proc;
114     procedure StringEndProc(EndChar:char);
115     procedure UnknownProc;
116     procedure MakeMethodTables;
117 
118   protected
119     Run: LongInt;
120     fStringLen: Integer;
121 
GetIdentCharsnull122     function GetIdentChars: TSynIdentChars; override;
GetSampleSourcenull123     function GetSampleSource: string; override;
IdentKindnull124     function IdentKind(MayBe: PChar): TtkTokenKind; virtual;
125     property Keywords: TStringlist read FKeywords;
126     property TokenID: TtkTokenKind read FTokenID;
127 
128   public
GetLanguageNamenull129     class function GetLanguageName: string; override;
130   public
131     constructor Create(AOwner: TComponent); override;
132     destructor Destroy; override;
GetDefaultAttributenull133     function GetDefaultAttribute(Index: integer): TSynHighlighterAttributes;
134       override;
GetEolnull135     function GetEol: Boolean; override;
GetRangenull136     function GetRange: Pointer; override;
GetTokenIDnull137     function GetTokenID: TtkTokenKind;
138     procedure SetLine(const NewValue: string;
139                       LineNumber: Integer); override;
GetKeywordIdentifiersnull140     function GetKeywordIdentifiers: TStringList; virtual;
GetTokennull141     function GetToken: string; override;
GetTokenAttributenull142     function GetTokenAttribute: TSynHighlighterAttributes; override;
GetTokenKindnull143     function GetTokenKind: integer; override;
GetTokenPosnull144     function GetTokenPos: Integer; override;
145     procedure Next; override;
146     property IdentChars;
147     procedure SetRange(Value: Pointer); override;
148     procedure ResetRange; override;
149     procedure GetTokenEx(out TokenStart: PChar; out TokenLength: integer); override;
150   published
151     property CommentAttri: TSynHighlighterAttributes read fCommentAttri
152     write fCommentAttri;
153     property IdentifierAttri: TSynHighlighterAttributes read fIdentifierAttri
154     write fIdentifierAttri;
155     property KeyAttri: TSynHighlighterAttributes read fKeyAttri write fKeyAttri;
156     property NonKeyAttri: TSynHighlighterAttributes read fNonKeyAttri
157       write fNonKeyAttri;
158     property SystemAttri: TSynHighlighterAttributes read fSystemAttri
159       write fSystemAttri;
160     property NumberAttri: TSynHighlighterAttributes read fNumberAttri
161     write fNumberAttri;
162     property HexAttri: TSynHighlighterAttributes read fHexAttri
163       write fHexAttri;
164     property OctalAttri: TSynHighlighterAttributes read fOctalAttri
165       write fOctalAttri;
166     property FloatAttri: TSynHighlighterAttributes read fFloatAttri
167       write fFloatAttri;
168     property SpaceAttri: TSynHighlighterAttributes read fSpaceAttri
169     write fSpaceAttri;
170     property StringAttri: TSynHighlighterAttributes read fStringAttri
171     write fStringAttri;
172     property DocStringAttri: TSynHighlighterAttributes read fDocStringAttri
173       write fDocStringAttri;
174     property SymbolAttri: TSynHighlighterAttributes read fSymbolAttri
175     write fSymbolAttri;
176     property ErrorAttri: TSynHighlighterAttributes read fErrorAttri
177       write fErrorAttri;
178   end;
179 
180 implementation
181 
182 uses
183   SynEditStrConst;
184 
185 var
186   GlobalKeywords: TStringList;
187 
TSynPythonSyn.GetKeywordIdentifiersnull188 function TSynPythonSyn.GetKeywordIdentifiers: TStringList;
189 const
190   // No need to localise keywords!
191 
192   // List of keywords
193   KEYWORDCOUNT = 29;
194   KEYWORDSIdents: array [1..KEYWORDCOUNT] of string =
195     (
196     'and',
197     'assert',
198     'break',
199     'class',
200     'continue',
201     'def',
202     'del',
203     'elif',
204     'else',
205     'except',
206     'exec',
207     'finally',
208     'for',
209     'from',
210     'global',
211     'if',
212     'import',
213     'in',
214     'is',
215     'lambda',
216     'not',
217     'or',
218     'pass',
219     'print',
220     'raise',
221     'return',
222     'try',
223     'while',
224     'yield'
225     );
226 
227   // List of non-keyword identifiers
228   NONKEYWORDCOUNT = 66;
229   NONKEYWORDS: array [1..NONKEYWORDCOUNT] of string =
230     (
231     '__future__',
232     '__import__',
233     'abs',
234     'apply',
235     'as',
236     'buffer',
237     'callable',
238     'chr',
239     'cmp',
240     'coerce',
241     'compile',
242     'complex',
243     'delattr',
244     'dict',
245     'dir',
246     'divmod',
247     'eval',
248     'execfile',
249     'False',
250     'file',
251     'filter',
252     'float',
253     'getattr',
254     'globals',
255     'hasattr',
256     'hash',
257     'help',
258     'hex',
259     'id',
260     'input',
261     'int',
262     'intern',
263     'isinstance',
264     'issubclass',
265     'iter',
266     'len',
267     'list',
268     'locals',
269     'long',
270     'None',
271     'NotImplemented',
272     'map',
273     'max',
274     'min',
275     'oct',
276     'open',
277     'ord',
278     'pow',
279     'range',
280     'raw_input',
281     'reduce',
282     'reload',
283     'repr',
284     'round',
285     'self',
286     'setattr',
287     'slice',
288     'str',
289     'True',
290     'tuple',
291     'type',
292     'unichr',
293     'unicode',
294     'vars',
295     'xrange',
296     'zip'
297     );
298 var
299   f: Integer;
300 begin
301   if not Assigned (GlobalKeywords) then begin
302     // Create the string list of keywords - only once
303     GlobalKeywords := TStringList.Create;
304 
305     for f := 1 to KEYWORDCOUNT do
306       GlobalKeywords.AddObject (KEYWORDSIdents[f],
307         TObject(Ord(tkKey)));
308     for f := 1 to NONKEYWORDCOUNT do
309       GlobalKeywords.AddObject (NONKEYWORDS[f],
310         TObject(Ord(tkNonKeyword)));
311   end; // if
312   Result := GlobalKeywords;
313 end;
314 
TSynPythonSyn.IdentKindnull315 function TSynPythonSyn.IdentKind(MayBe: PChar): TtkTokenKind;
316 var
317   index: Integer;
318   temp: PChar;
319   s: string;
320 
321 begin
322   // Extract the identifier out - it is assumed to terminate in a
323   //   non-alphanumeric character
324   fToIdent := MayBe;
325   temp := MayBe;
326   while temp^ in IDENTIFIER_CHARS do
327     Inc (temp);
328   fStringLen := temp - fToIdent;
329 
330   // Check to see if it is a keyword
331   SetString (s, fToIdent, fStringLen);
332   index := FKeywords.IndexOf (s);
333 
334   if index <> -1 then
335     Result := TtkTokenKind (PtrInt(FKeywords.Objects[index]))
336 
337   // Check if it is a system identifier (__*__)
338   else if (fStringLen >= 5) and
339      (MayBe[0] = '_') and (MayBe[1] = '_') and (MayBe[2] <> '_') and
340      (MayBe[fStringLen-1] = '_') and (MayBe[fStringLen-2] = '_') and
341      (MayBe[fStringLen-3] <> '_') then
342     Result := tkSystemDefined
343 
344   // Else, hey, it is an ordinary run-of-the-mill identifier!
345   else
346     Result := tkIdentifier;
347 end;
348 
349 procedure TSynPythonSyn.MakeMethodTables;
350 var
351   I: Char;
352 begin
353   for I := #0 to #255 do
354     case I of
355       '&', '}', '{', ':', ',', ']', '[', '*', '`',
356       '^', ')', '(', ';', '/', '=', '-', '+', '!', '\',
357       '%', '|', '~' :
358         fProcTable[I] := @SymbolProc;
359       #13: fProcTable[I] := @CRProc;
360       '#': fProcTable[I] := @CommentProc;
361       '>': fProcTable[I] := @GreaterProc;
362       'A'..'Q', 'S', 'T', 'V'..'Z', 'a'..'q', 's', 't', 'v'..'z', '_': fProcTable[I] := @IdentProc;
363       #10: fProcTable[I] := @LFProc;
364       '<': fProcTable[I] := @LowerProc;
365       #0: fProcTable[I] := @NullProc;
366       '.', '0'..'9': fProcTable[I] := @NumberProc;
367       #1..#9, #11, #12, #14..#32:
368         fProcTable[I] := @SpaceProc;
369       'r', 'R': fProcTable[I] := @PreStringProc;
370       'u', 'U': fProcTable[I] := @UnicodeStringProc;
371       '''': fProcTable[I] := @StringProc;
372       '"': fProcTable[I] := @String2Proc;
373     else
374       fProcTable[I] := @UnknownProc;
375     end;
376 end;
377 
378 constructor TSynPythonSyn.Create(AOwner: TComponent);
379 begin
380   inherited Create(AOwner);
381 
382   FKeywords := THashedStringList.Create;
383   FKeywords.CaseSensitive := True;
384   FKeywords.Duplicates := dupError;
385   FKeywords.Assign (GetKeywordIdentifiers);
386 
387   fRange := rsUnknown;
388   fCommentAttri := TSynHighlighterAttributes.Create(@SYNS_AttrComment, SYNS_XML_AttrComment);
389   fCommentAttri.Foreground := clGray;
390   fCommentAttri.Style := [fsItalic];
391   AddAttribute(fCommentAttri);
392   fIdentifierAttri := TSynHighlighterAttributes.Create(@SYNS_AttrIdentifier, SYNS_XML_AttrIdentifier);
393   AddAttribute(fIdentifierAttri);
394   fKeyAttri := TSynHighlighterAttributes.Create(@SYNS_AttrReservedWord, SYNS_XML_AttrReservedWord);
395   fKeyAttri.Style := [fsBold];
396   AddAttribute(fKeyAttri);
397   fNonKeyAttri := TSynHighlighterAttributes.Create(@SYNS_AttrNonReservedKeyword, SYNS_XML_AttrNonReservedKeyword);
398   fNonKeyAttri.Foreground := clNavy;
399   fNonKeyAttri.Style := [fsBold];
400   AddAttribute (fNonKeyAttri);
401   fSystemAttri := TSynHighlighterAttributes.Create(@SYNS_AttrSystem, SYNS_XML_AttrSystem);
402   fSystemAttri.Style := [fsBold];
403   AddAttribute (fSystemAttri);
404   fNumberAttri := TSynHighlighterAttributes.Create(@SYNS_AttrNumber, SYNS_XML_AttrNumber);
405   fNumberAttri.Foreground := clBlue;
406   AddAttribute(fNumberAttri);
407   fHexAttri := TSynHighlighterAttributes.Create(@SYNS_AttrHexadecimal, SYNS_XML_AttrHexadecimal);
408   fHexAttri.Foreground := clBlue;
409   AddAttribute(fHexAttri);
410   fOctalAttri := TSynHighlighterAttributes.Create(@SYNS_AttrOctal, SYNS_XML_AttrOctal);
411   fOctalAttri.Foreground := clBlue;
412   AddAttribute(fOctalAttri);
413   fFloatAttri := TSynHighlighterAttributes.Create(@SYNS_AttrFloat, SYNS_XML_AttrFloat);
414   fFloatAttri.Foreground := clBlue;
415   AddAttribute(fFloatAttri);
416   fSpaceAttri := TSynHighlighterAttributes.Create(@SYNS_AttrSpace, SYNS_XML_AttrSpace);
417   AddAttribute(fSpaceAttri);
418   fStringAttri := TSynHighlighterAttributes.Create(@SYNS_AttrString, SYNS_XML_AttrString);
419   fStringAttri.Foreground := clBlue;
420   AddAttribute(fStringAttri);
421   fDocStringAttri := TSynHighlighterAttributes.Create(@SYNS_AttrDocumentation, SYNS_XML_AttrDocumentation);
422   fDocStringAttri.Foreground := clTeal;
423   AddAttribute(fDocStringAttri);
424   fSymbolAttri := TSynHighlighterAttributes.Create(@SYNS_AttrSymbol, SYNS_XML_AttrSymbol);
425   AddAttribute(fSymbolAttri);
426   fErrorAttri := TSynHighlighterAttributes.Create(@SYNS_AttrSyntaxError, SYNS_XML_AttrSyntaxError);
427   fErrorAttri.Foreground := clRed;
428   AddAttribute(fErrorAttri);
429   SetAttributesOnChange(@DefHighlightChange);
430   MakeMethodTables;
431   fDefaultFilter := SYNS_FilterPython;
432 end; { Create }
433 
434 destructor TSynPythonSyn.Destroy;
435 begin
436   FKeywords.Free;
437 
438   inherited;
439 end;
440 
441 procedure TSynPythonSyn.SetLine(const NewValue: string;
442   LineNumber: Integer);
443 begin
444   inherited;
445   fLine := PChar(NewValue);
446   Run := 0;
447   fLineNumber := LineNumber;
448   Next;
449 end; { SetLine }
450 
451 procedure TSynPythonSyn.GetTokenEx(out TokenStart: PChar;
452   out TokenLength: integer);
453 begin
454   TokenLength:=Run-fTokenPos;
455   TokenStart:=FLine + fTokenPos;
456 end;
457 
458 procedure TSynPythonSyn.SymbolProc;
459 begin
460   inc(Run);
461   fTokenID := tkSymbol;
462 end;
463 
464 procedure TSynPythonSyn.CRProc;
465 begin
466   fTokenID := tkSpace;
467   case FLine[Run + 1] of
468     #10: inc(Run, 2);
469   else
470     inc(Run);
471   end;
472 end;
473 
474 procedure TSynPythonSyn.CommentProc;
475 begin
476   fTokenID := tkComment;
477   inc(Run);
478   while not (FLine[Run] in [#13, #10, #0]) do
479     inc(Run);
480 end;
481 
482 procedure TSynPythonSyn.GreaterProc;
483 begin
484   case FLine[Run + 1] of
485     '=': begin
486         inc(Run, 2);
487         fTokenID := tkSymbol;
488       end;
489   else begin
490       inc(Run);
491       fTokenID := tkSymbol;
492     end;
493   end;
494 end;
495 
496 procedure TSynPythonSyn.IdentProc;
497 begin
498   fTokenID := IdentKind((fLine + Run));
499   inc(Run, fStringLen);
500 end;
501 
502 procedure TSynPythonSyn.LFProc;
503 begin
504   fTokenID := tkSpace;
505   inc(Run);
506 end;
507 
508 procedure TSynPythonSyn.LowerProc;
509 begin
510   case FLine[Run + 1] of
511     '=': begin
512         inc(Run, 2);
513         fTokenID := tkSymbol;
514       end;
515     '>': begin
516         inc(Run, 2);
517         fTokenID := tkSymbol;
518       end
519   else begin
520       inc(Run);
521       fTokenID := tkSymbol;
522     end;
523   end;
524 end;
525 
526 procedure TSynPythonSyn.NullProc;
527 begin
528   fTokenID := tkNull;
529 end;
530 
531 procedure TSynPythonSyn.NumberProc;
532 const
533   INTCHARS = ['0' .. '9'];
534   HEXCHARS = ['a' .. 'f', 'A' .. 'F'] + INTCHARS;
535   OCTCHARS = ['0' .. '7'];
536   HEXINDICATOR = ['x', 'X'];
537   LONGINDICATOR = ['l', 'L'];
538   IMAGINARYINDICATOR = ['j', 'J'];
539   EXPONENTINDICATOR = ['e', 'E'];
540   EXPONENTSIGN = ['+', '-'];
541   DOT = '.';
542   ZERO = '0';
543 
544 type
545   TNumberState =
546     (
547     nsStart,
548     nsDotFound,
549     nsFloatNeeded,
550     nsHex,
551     nsOct,
552     nsExpFound
553     );
554 
555 var
556   temp: Char;
557   State: TNumberState;
558 
CheckSpecialCasesnull559   function CheckSpecialCases: Boolean;
560   begin
561     case temp of
562       // Look for dot (.)
563       DOT: begin
564         // .45
565         if FLine[Run] in INTCHARS then begin
566           Inc (Run);
567           fTokenID := tkFloat;
568           State := nsDotFound;
569 
570         // Non-number dot
571         end else begin
572           // Ellipsis
573           if (FLine[Run] = DOT) and (FLine[Run+1] = DOT) then
574             Inc (Run, 2);
575           fTokenID := tkSymbol;
576           Result := False;
577           Exit;
578         end; // if
579       end; // DOT
580 
581       // Look for zero (0)
582       ZERO: begin
583         temp := FLine[Run];
584         // 0x123ABC
585         if temp in HEXINDICATOR then begin
586           Inc (Run);
587           fTokenID := tkHex;
588           State := nsHex;
589         // 0.45
590         end else if temp = DOT then begin
591           Inc (Run);
592           State := nsDotFound;
593           fTokenID := tkFloat;
594         end else if temp in INTCHARS then begin
595           Inc (Run);
596           // 0123 or 0123.45
597           if temp in OCTCHARS then begin
598             fTokenID := tkOct;
599             State := nsOct;
600           // 0899.45
601           end else begin
602             fTokenID := tkFloat;
603             State := nsFloatNeeded;
604           end; // if
605         end; // if
606       end; // ZERO
607     end; // case
608 
609     Result := True;
610   end; // CheckSpecialCases
611 
HandleBadNumbernull612   function HandleBadNumber: Boolean;
613   begin
614     Result := False;
615     fTokenID := tkUnknown;
616     // Ignore all tokens till end of "number"
617     while FLine[Run] in (IDENTIFIER_CHARS + ['.']) do
618       Inc (Run);
619   end; // HandleBadNumber
620 
HandleExponentnull621   function HandleExponent: Boolean;
622   begin
623     State := nsExpFound;
624     fTokenID := tkFloat;
625     // Skip e[+/-]
626     if FLine[Run+1] in EXPONENTSIGN then
627       Inc (Run);
628     // Invalid token : 1.0e
629     if not (FLine[Run+1] in INTCHARS) then begin
630       Inc (Run);
631       Result := HandleBadNumber;
632       Exit;
633     end; // if
634 
635     Result := True;
636   end; // HandleExponent
637 
HandleDotnull638   function HandleDot: Boolean;
639   begin
640     // Check for ellipsis
641     Result := (FLine[Run+1] <> DOT) or (FLine[Run+2] <> DOT);
642     if Result then begin
643       State := nsDotFound;
644       fTokenID := tkFloat;
645     end; // if
646   end; // HandleDot
647 
CheckStartnull648   function CheckStart: Boolean;
649   begin
650     // 1234
651     if temp in INTCHARS then begin
652       Result := True;
653     //123e4
654     end else if temp in EXPONENTINDICATOR then begin
655       Result := HandleExponent;
656     // 123.45j
657     end else if temp in IMAGINARYINDICATOR then begin
658       Inc (Run);
659       fTokenID := tkFloat;
660       Result := False;
661     // 123.45
662     end else if temp = DOT then begin
663       Result := HandleDot;
664     // Error!
665     end else if temp in IDENTIFIER_CHARS then begin
666       Result := HandleBadNumber;
667     // End of number
668     end else begin
669       Result := False;
670     end; // if
671   end; // CheckStart
672 
CheckDotFoundnull673   function CheckDotFound: Boolean;
674   begin
675     // 1.0e4
676     if temp in EXPONENTINDICATOR then begin
677       Result := HandleExponent;
678     // 123.45
679     end else if temp in INTCHARS then begin
680       Result := True;
681     // 123.45j
682     end else if temp in IMAGINARYINDICATOR then begin
683       Inc (Run);
684       Result := False;
685     // 123.45.45: Error!
686     end else if temp = DOT then begin
687       Result := False;
688       if HandleDot then
689         HandleBadNumber;
690     // Error!
691     end else if temp in IDENTIFIER_CHARS then begin
692       Result := HandleBadNumber;
693     // End of number
694     end else begin
695       Result := False;
696     end; // if
697   end; // CheckDotFound
698 
CheckFloatNeedednull699   function CheckFloatNeeded: Boolean;
700   begin
701     // 091.0e4
702     if temp in EXPONENTINDICATOR then begin
703       Result := HandleExponent;
704     // 0912345
705     end else if temp in INTCHARS then begin
706       Result := True;
707     // 09123.45
708     end else if temp = DOT then begin
709       Result := HandleDot or HandleBadNumber; // Bad octal
710     // 09123.45j
711     end else if temp in IMAGINARYINDICATOR then begin
712       Inc (Run);
713       Result := False;
714     // End of number (error: Bad oct number) 0912345
715     end else begin
716       Result := HandleBadNumber;
717     end;
718   end; // CheckFloatNeeded
719 
CheckHexnull720   function CheckHex: Boolean;
721   begin
722     // 0x123ABC
723     if temp in HEXCHARS then begin
724       Result := True;
725     // 0x123ABCL
726     end else if temp in LONGINDICATOR then begin
727       Inc (Run);
728       Result := False;
729     // 0x123.45: Error!
730     end else if temp = DOT then begin
731       Result := False;
732       if HandleDot then
733         HandleBadNumber;
734     // Error!
735     end else if temp in IDENTIFIER_CHARS then begin
736       Result := HandleBadNumber;
737     // End of number
738     end else begin
739       Result := False;
740     end; // if
741   end; // CheckHex
742 
CheckOctnull743   function CheckOct: Boolean;
744   begin
745     // 012345
746     if temp in INTCHARS then begin
747       if not (temp in OCTCHARS) then begin
748         State := nsFloatNeeded;
749         fTokenID := tkFloat;
750       end; // if
751       Result := True;
752     // 012345L
753     end else if temp in LONGINDICATOR then begin
754       Inc (Run);
755       Result := False;
756     // 0123e4
757     end else if temp in EXPONENTINDICATOR then begin
758       Result := HandleExponent;
759     // 0123j
760     end else if temp in IMAGINARYINDICATOR then begin
761       Inc (Run);
762       fTokenID := tkFloat;
763       Result := False;
764     // 0123.45
765     end else if temp = DOT then begin
766       Result := HandleDot;
767     // Error!
768     end else if temp in IDENTIFIER_CHARS then begin
769       Result := HandleBadNumber;
770     // End of number
771     end else begin
772       Result := False;
773     end; // if
774   end; // CheckOct
775 
CheckExpFoundnull776   function CheckExpFound: Boolean;
777   begin
778     // 1e+123
779     if temp in INTCHARS then begin
780       Result := True;
781     // 1e+123j
782     end else if temp in IMAGINARYINDICATOR then begin
783       Inc (Run);
784       Result := False;
785     // 1e4.5: Error!
786     end else if temp = DOT then begin
787       Result := False;
788       if HandleDot then
789         HandleBadNumber;
790     // Error!
791     end else if temp in IDENTIFIER_CHARS then begin
792       Result := HandleBadNumber;
793     // End of number
794     end else begin
795       Result := False;
796     end; // if
797   end; // CheckExpFound
798 
799 begin
800   State := nsStart;
801   fTokenID := tkNumber;
802 
803   temp := FLine[Run];
804   Inc (Run);
805 
806   // Special cases
807   if not CheckSpecialCases then
808     Exit;
809 
810   // Use a state machine to parse numbers
811   while True do begin
812     temp := FLine[Run];
813 
814     case State of
815       nsStart:
816         if not CheckStart then Exit;
817       nsDotFound:
818         if not CheckDotFound then Exit;
819       nsFloatNeeded:
820         if not CheckFloatNeeded then Exit;
821       nsHex:
822         if not CheckHex then Exit;
823       nsOct:
824         if not CheckOct then Exit;
825       nsExpFound:
826         if not CheckExpFound then Exit;
827     end; // case
828 
829     Inc (Run);
830   end; // while
831 
832 {
833 begin
834   while FLine[Run] in ['0'..'9', '.', 'e', 'E'] do begin
835     case FLine[Run] of
836       '.':
837         if FLine[Run + 1] = '.' then break;
838     end;
839     inc(Run);
840   end;
841 }
842 end;
843 
844 procedure TSynPythonSyn.SpaceProc;
845 begin
846   inc(Run);
847   fTokenID := tkSpace;
848   while FLine[Run] in [#1..#9, #11, #12, #14..#32] do
849     inc(Run);
850 end;
851 
852 procedure TSynPythonSyn.String2Proc;
853 var fBackslashCount:integer;
854 begin
855   fTokenID := tkString;
856   if (FLine[Run + 1] = '"') and (FLine[Run + 2] = '"') then begin
857     fTokenID := tkTripleQuotedString;
858     inc(Run, 3);
859 
860     fRange:=rsMultilineString2;
861     while fLine[Run] <> #0 do begin
862       case fLine[Run] of
863 
864         '\':begin
865                { If we're looking at a backslash, and the following character is an
866                end quote, and it's preceeded by an odd number of backslashes, then
867                it shouldn't mark the end of the string.  If it's preceeded by an
868                even number, then it should. !!!THIS RULE DOESNT APPLY IN RAW STRINGS}
869                if FLine[Run + 1] = '"' then
870                  begin
871                    fBackslashCount := 1;
872 
873                    while ((Run > fBackslashCount) and (FLine[Run - fBackslashCount] = '\')) do
874                      fBackslashCount := fBackslashCount + 1;
875 
876                    if (fBackslashCount mod 2 = 1) then inc(Run)
877                end;
878                inc(Run);
879             end;// '\':
880 
881         '"':
882           if (fLine[Run + 1] = '"') and (fLine[Run + 2] = '"') then begin
883             fRange := rsUnKnown;
884             inc(Run, 3);
885             EXIT;
886           end else
887             inc(Run);
888         #10:EXIT;
889         #13:EXIT;
890         else
891           inc(Run);
892       end;
893     end;
894   end
895       else //if short string
896   repeat
897     case FLine[Run] of
898       #0, #10, #13 : begin
899         if FLine[Run-1] = '\' then begin
900           fStringStarter := '"';
901           fRange := rsMultilineString3;
902         end;
903         BREAK;
904         end;
905       {The same backslash stuff above...}
906       '\':begin
907              if FLine[Run + 1] = '"' then
908                begin
909                  fBackslashCount := 1;
910 
911                  while ((Run > fBackslashCount) and (FLine[Run - fBackslashCount] = '\')) do
912                    fBackslashCount := fBackslashCount + 1;
913 
914                  if (fBackslashCount mod 2 = 1) then inc(Run)
915              end;
916              inc(Run);
917           end;// '\':
918 
919       else inc(Run);
920     end; //case
921   until (FLine[Run] = '"');
922   if FLine[Run] <> #0 then inc(Run);
923 end;
924 
925 procedure TSynPythonSyn.PreStringProc;
926 var
927   temp: Char;
928 
929 begin
930   // Handle python raw strings
931   // r""
932   temp := FLine[Run + 1];
933   if temp = '''' then begin
934     Inc (Run);
935     StringProc;
936   end else if temp = '"' then begin
937     Inc (Run);
938     String2Proc;
939   end else begin
940     // If not followed by quote char, must be ident
941     IdentProc;
942   end; // if
943 end;
944 
945 procedure TSynPythonSyn.UnicodeStringProc;
946 begin
947   // Handle python raw and unicode strings
948   // Valid syntax: u"", or ur""
949   if (FLine[Run + 1] in ['r', 'R']) and (FLine[Run + 2] in ['''', '"']) then
950     // for ur, Remove the "u" and...
951     Inc (Run);
952   // delegate to raw strings
953   PreStringProc;
954 end;
955 
956 procedure TSynPythonSyn.StringProc;
957 var fBackslashCount:integer;
958 begin
959   fTokenID := tkString;
960   if (FLine[Run + 1] = #39) and (FLine[Run + 2] = #39) then begin
961     fTokenID := tkTripleQuotedString;
962     inc(Run, 3);
963 
964     fRange:=rsMultilineString;
965     while fLine[Run] <> #0 do begin
966       case fLine[Run] of
967 
968         '\': begin
969              { If we're looking at a backslash, and the following character is an
970              end quote, and it's preceeded by an odd number of backslashes, then
971              it shouldn't mark the end of the string.  If it's preceeded by an
972              even number, then it should. !!!THIS RULE DOESNT APPLY IN RAW STRINGS}
973               if FLine[Run + 1] = #39 then
974                 begin
975                   fBackslashCount := 1;
976 
977                   while ((Run > fBackslashCount) and (FLine[Run - fBackslashCount] = '\')) do
978                     fBackslashCount := fBackslashCount + 1;
979 
980                   if (fBackslashCount mod 2 = 1) then inc(Run)
981               end;
982               inc(Run);
983             end;// '\':
984 
985         #39:
986           if (fLine[Run + 1] = #39) and (fLine[Run + 2] = #39) then begin
987             fRange := rsUnKnown;
988             inc(Run, 3);
989             EXIT;
990           end else
991             inc(Run);
992         #10: EXIT;
993         #13: EXIT;
994         else
995           inc(Run);
996       end;
997     end;
998   end
999       else //if short string
1000   repeat
1001     case FLine[Run] of
1002       #0, #10, #13 : begin
1003         if FLine[Run-1] = '\' then begin
1004           fStringStarter := #39;
1005           fRange := rsMultilineString3;
1006         end;
1007         BREAK;
1008         end;
1009 
1010       {The same backslash stuff above...}
1011       '\':begin
1012              if FLine[Run + 1] = #39 then
1013                begin
1014                  fBackslashCount := 1;
1015 
1016                  while ((Run > fBackslashCount) and (FLine[Run - fBackslashCount] = '\')) do
1017                    fBackslashCount := fBackslashCount + 1;
1018 
1019                  if (fBackslashCount mod 2 = 1) then inc(Run)
1020              end;
1021              inc(Run);
1022           end;// '\':
1023 
1024       else inc(Run);
1025     end; //case
1026   until (FLine[Run] = #39);
1027   if FLine[Run] <> #0 then inc(Run);
1028 end;
1029 
1030 procedure TSynPythonSyn.StringEndProc(EndChar:char);
1031 var fBackslashCount:integer;
1032 begin
1033   if fRange = rsMultilineString3 then
1034     fTokenID := tkString
1035   else
1036     fTokenID := tkTripleQuotedString;
1037 
1038   case FLine[Run] of
1039     #0:
1040       begin
1041         NullProc;
1042         EXIT;
1043       end;
1044     #10:
1045       begin
1046         LFProc;
1047         EXIT;
1048     end;
1049     #13:
1050       begin
1051         CRProc;
1052         EXIT;
1053       end;
1054   end;
1055 
1056   if fRange = rsMultilineString3 then begin
1057     repeat
1058       if FLine[Run]=fStringStarter then begin
1059         inc(Run);
1060         fRange:=rsUnknown;
1061         EXIT;
1062       end else if FLine[Run]='\' then ;  {The same backslash stuff above...}
1063           begin
1064              if FLine[Run + 1] = fStringStarter then
1065                begin
1066                  fBackslashCount := 1;
1067 
1068                  while ((Run > fBackslashCount) and (FLine[Run - fBackslashCount] = '\')) do
1069                    fBackslashCount := fBackslashCount + 1;
1070 
1071                  if (fBackslashCount mod 2 = 1) then inc(Run);
1072              end;
1073            end;// if FLine[Run]...
1074 
1075       inc(Run);
1076     until (FLine[Run] in [#0, #10, #13]);
1077     if FLine[Run-1]<>'\' then begin
1078       fRange:=rsUnknown;
1079       EXIT;
1080     end;
1081   end else
1082   repeat
1083     if FLine[Run] = '\' then
1084     begin
1085        if FLine[Run + 1] = EndChar then
1086          begin
1087            fBackslashCount := 1;
1088 
1089            while ((Run > fBackslashCount) and (FLine[Run - fBackslashCount] = '\')) do
1090              fBackslashCount := fBackslashCount + 1;
1091 
1092            if (fBackslashCount mod 2 = 1) then inc(Run,2);
1093        end;
1094      end;// if FLine[Run]...
1095     if (FLine[Run]=EndChar) and (FLine[Run+1]=EndChar) and (FLine[Run+2]=EndChar) then begin
1096       inc(Run,3);
1097       fRange:=rsUnknown;
1098       EXIT;
1099     end;
1100     inc(Run);
1101   until (FLine[Run] in [#0, #10, #13]);
1102 end;
1103 
1104 procedure TSynPythonSyn.UnknownProc;
1105 begin
1106   inc(Run);
1107   while (fLine[Run] in [#128..#191]) OR // continued utf8 subcode
1108    ((fLine[Run]<>#0) and (fProcTable[fLine[Run]] = @UnknownProc)) do inc(Run);
1109   fTokenID := tkUnknown;
1110 end;
1111 
1112 procedure TSynPythonSyn.Next;
1113 begin
1114   fTokenPos := Run;
1115 
1116   case fRange of
1117     rsMultilineString:
1118       StringEndProc(#39);
1119     rsMultilineString2:
1120       StringEndProc('"');
1121     rsMultilineString3:
1122       StringEndProc(fStringStarter);
1123     else
1124       fProcTable[fLine[Run]];
1125   end;
1126 end;
1127 
TSynPythonSyn.GetDefaultAttributenull1128 function TSynPythonSyn.GetDefaultAttribute(Index: integer): TSynHighlighterAttributes;
1129 begin
1130   case Index of
1131     SYN_ATTR_COMMENT: Result := fCommentAttri;
1132     SYN_ATTR_KEYWORD: Result := fKeyAttri;
1133     SYN_ATTR_WHITESPACE: Result := fSpaceAttri;
1134     SYN_ATTR_SYMBOL: Result := fSymbolAttri;
1135     SYN_ATTR_NUMBER: Result := fNumberAttri;
1136   else
1137     Result := nil;
1138   end;
1139 end;
1140 
GetEolnull1141 function TSynPythonSyn.GetEol: Boolean;
1142 begin
1143   Result := fTokenId = tkNull;
1144 end;
1145 
TSynPythonSyn.GetRangenull1146 function TSynPythonSyn.GetRange: Pointer;
1147 begin
1148   Result := Pointer(PtrInt(fRange));
1149 end;
1150 
TSynPythonSyn.GetTokennull1151 function TSynPythonSyn.GetToken: string;
1152 var
1153   Len: LongInt;
1154 begin
1155   Result := '';
1156   Len := Run - fTokenPos;
1157   SetString(Result, (FLine + fTokenPos), Len);
1158 end;
1159 
GetTokenIDnull1160 function TSynPythonSyn.GetTokenID: TtkTokenKind;
1161 begin
1162   Result := fTokenId;
1163 end;
1164 
GetTokenAttributenull1165 function TSynPythonSyn.GetTokenAttribute: TSynHighlighterAttributes;
1166 begin
1167   case fTokenID of
1168     tkComment: Result := fCommentAttri;
1169     tkIdentifier: Result := fIdentifierAttri;
1170     tkKey: Result := fKeyAttri;
1171     tkNonKeyword: Result := fNonKeyAttri;
1172     tkSystemDefined: Result := fSystemAttri;
1173     tkNumber: Result := fNumberAttri;
1174     tkHex: Result := fHexAttri;
1175     tkOct: Result := fOctalAttri;
1176     tkFloat: Result := fFloatAttri;
1177     tkSpace: Result := fSpaceAttri;
1178     tkString: Result := fStringAttri;
1179     tkTripleQuotedString: Result := fDocStringAttri;
1180     tkSymbol: Result := fSymbolAttri;
1181     tkUnknown: Result := fErrorAttri;
1182   else
1183     Result := nil;
1184   end;
1185 end;
1186 
GetTokenKindnull1187 function TSynPythonSyn.GetTokenKind: integer;
1188 begin
1189   Result := Ord(fTokenId);
1190 end;
1191 
TSynPythonSyn.GetTokenPosnull1192 function TSynPythonSyn.GetTokenPos: Integer;
1193 begin
1194   Result := fTokenPos;
1195 end;
1196 
1197 procedure TSynPythonSyn.ResetRange;
1198 begin
1199   fRange := rsUnknown;
1200 end;
1201 
1202 procedure TSynPythonSyn.SetRange(Value: Pointer);
1203 begin
1204   fRange := TRangeState(PtrUInt(Value));
1205 end;
1206 
GetIdentCharsnull1207 function TSynPythonSyn.GetIdentChars: TSynIdentChars;
1208 begin
1209   Result := TSynValidStringChars;
1210 end;
1211 
TSynPythonSyn.GetLanguageNamenull1212 class function TSynPythonSyn.GetLanguageName: string;
1213 begin
1214   Result := SYNS_LangPython;
1215 end;
1216 
GetSampleSourcenull1217 function TSynPythonSyn.GetSampleSource: string;
1218 begin
1219   Result :=
1220     '#!/usr/local/bin/python'#13#10 +
1221     'import string, sys'#13#10 +
1222     '""" If no arguments were given, print a helpful message """'#13#10 +
1223     'if len(sys.argv)==1:'#13#10 +
1224     '    print ''Usage: celsius temp1 temp2 ...'''#13#10 +
1225     '    sys.exit(0)';
1226 end;
1227 
1228 initialization
1229   RegisterPlaceableHighlighter(TSynPythonSyn);
1230 
1231 finalization
1232   GlobalKeywords.Free;
1233 end.
1234 
1235