1 {                               TSynFacilSyn
2 Unidad principal de SynfacilSyn.
3 
4 Queda pendiente incluir el procesamiento de los paréntesis en las expresiones regulares,
5 como una forma sencilla de definir bloques de Regex, sin tener que usar la definición
6 avanzada. También se podría ver si se puede mejorar el soporte de Regex, sobre todo para el
7 caso de expresiones como ".*a".
8 
9 
10                                     Por Tito Hinostroza  15/09/2015 - Lima Perú
11 }
12 unit SynFacilHighlighter;
13 {$mode objfpc}{$H+}
14 interface
15 uses
16   Classes, SysUtils, Graphics, SynEditHighlighter, DOM, XMLRead,
17   Dialogs, Fgl, strings, Lazlogger, SynEditHighlighterFoldBase, LCLIntf,
18   SynFacilBasic;
19 const
20   COL_TRANSPAR = $FDFEFF;  //color transparente
21 type
22 
23   //Para manejo del plegado
24   TFaListBlocks = specialize TFPGObjectList<TFaSynBlock>;   //lista de bloques
25 
26   //Descripción de token. Usado solamente para el trabajo del método ExploreLine()
27   TFaTokInfo = record
28      txt    : string;        //texto del token
29      TokPos : integer;       //posición del token dentro de la línea
30      TokTyp : integer;       //tipo de token
31      IsIDentif: boolean;     //para saber si es identificador
32      posIni : integer;       //posición de inicio en la línea
33      length : integer;       //tamaño del token (en bytes)
34      curBlk : TFaSynBlock;   //referencia al bloque del token
35   end;
36   TATokInfo = array of TFaTokInfo;
37 
38   //Permite leer el estado actual del resaltador. Considera la posición actual de la
39   //exploración y el estado del rango, NO CONSIDERA el estado de los bloques de
40   //plegado. Se usa cuando se hace trabajar al resaltador como analizador léxico.
41   TFaLexerState = record
42     //propiedades fijadas al inicio de la línea y no cambian en toda la línea.
43 //    fLine      : PChar;         //puntero a línea de trabajo.
44 //    tamLin     : integer;       //tamaño de línea actual
45     LineText   : string;        //línea de trabajo
46     LineIndex  : integer;       //el número de línea actual
47     //propiedades que van cambiando conforme se avanza en la exploración de la línea
48     posTok     : integer;       //para identificar el ordinal del token en una línea
49     BlkToClose : TFaSynBlock;   //bandera-variable para posponer el cierre de un bloque
50     posIni     : Integer;       //índice a inicio de token
51     posFin     : Integer;       //índice a siguiente token
52     fRange     : ^TTokSpec;    //para trabajar con tokens multilínea
53     fTokenID   : integer;      //Id del token actual
54   end;
55 
56   { TSynFacilSyn }
57 
58   TSynFacilSyn = class(TSynFacilSynBase)
59   protected  //Variables internas
60     lisBlocks  : TFaListBlocks; //lista de bloques de sintaxis
61     delTok     : string;        //delimitador del bloque actual
62     folTok     : boolean;       //indica si hay "folding" que cerrar en token delimitado actual
63     chrEsc     : char;          //indica si hay caracter de escape en token delimitado actual (#0 si no hay)
64     nTokenCon  : integer;       //cantidad de tokens por contenido
65     fRange     : TPtrTokEspec;  //para trabajar con tokens multilínea
66     CloseThisBlk: TFaSynBlock;   //bandera-variable para posponer el cierre de un bloque
67     OnFirstTok : procedure of object;
68     procedure SetTokContent(tc: tFaTokContent; dStart: string;
69       TypDelim: TFaTypeDelim; typToken: integer);
70     //Manejo de bloques
71     procedure StartBlock(ABlockType: Pointer; IncreaseLevel: Boolean); inline;
72     procedure EndBlock(DecreaseLevel: Boolean); inline;
73     procedure StartBlockAndFirstSec(const blk, firstSec: TfaSynBlock);
74     procedure StartBlockFa(const blk: TfaSynBlock);
75     procedure EndBlockFa(const blk: TfaSynBlock);
TopBlocknull76     function TopBlock: TFaSynBlock;
TopBlockOpacnull77     function TopBlockOpac: TFaSynBlock;
78   protected  //Funciones de bajo nivel
CreaBuscIdeEspecnull79     function CreaBuscIdeEspec(out mat: TPtrATokEspec; cad: string; out i: integer;
80       TokPos: integer = 0): boolean;
CreaBuscSymEspecnull81     function CreaBuscSymEspec(out mat: TPtrATokEspec; cad: string; out i: integer;
82       TokPos: integer = 0): boolean;
CreaBuscEspecnull83     function CreaBuscEspec(out tok: TPtrTokEspec; cad: string; TokPos: integer
84       ): boolean;
85     procedure TableIdent(iden: string; out mat: TPtrATokEspec; out
86       met: TFaProcMetTable);
87     procedure FirstXMLExplor(doc: TXMLDocument);
ProcXMLBlocknull88     function ProcXMLBlock(nodo: TDOMNode; blqPad: TFaSynBlock): boolean;
ProcXMLSectionnull89     function ProcXMLSection(nodo: TDOMNode; blqPad: TFaSynBlock): boolean;
90   public    //funciones públicas de alto nivel
91 //    Err       : string;         //Mensaje de error
92     LangName  : string;         //Nombre del lengauje
93     Extensions: String;         //Extensiones de archivo
94     MainBlk   : TFaSynBlock;    //Bloque global
95     MulTokBlk : TFaSynBlock;    //Bloque reservado para bloques multitokens
96     ColBlock  : TFaColBlock;    //Coloreado por bloques
97     procedure ClearMethodTables; //Limpia la tabla de métodos
98     //Definición de tokens por contenido
DefTokContentnull99     function DefTokContent(dStart: string; typToken: integer): tFaTokContent;
100     procedure DefTokContent(dStart, Content: string; typToken: integer;
101       Complete: boolean = false);
102     //Manejo de identificadores especiales
103     procedure ClearSpecials;        //Limpia identif, y símbolos especiales
104     procedure AddIdentSpec(iden: string; tokTyp: integer; TokPos: integer=0);
105     procedure AddIdentSpecList(listIden: string; tokTyp: integer; TokPos: integer=0);
106     procedure AddKeyword(iden: string);
107     procedure AddSymbSpec(symb: string; tokTyp: integer; TokPos: integer=0);
108     procedure AddSymbSpecList(listSym: string; tokTyp: integer; TokPos: integer=0);
109     procedure DefTokDelim(dStart, dEnd: string; tokTyp: integer;
110       tipDel: TFaTypeDelim=tdUniLin; havFolding: boolean=false; chrEscape: char=#0);
111     procedure RebuildSymbols;
112     procedure LoadFromStream(Stream: TStream); virtual;                                    //load highlighter from a stream
113     procedure LoadFromResourceName(Instance: THandle; const ResName: String); virtual; //load highlighter from a resource
114     procedure LoadFromFile(const Filename: string); virtual;                                      //Para cargar sintaxis
115     procedure Rebuild; virtual;
116 
117     procedure AddIniBlockToTok(dStart: string; TokPos: integer; blk: TFaSynBlock);
118     procedure AddFinBlockToTok(dEnd: string; TokPos: integer; blk: TFaSynBlock);
119     procedure AddIniSectToTok(dStart: string; TokPos: integer; blk: TFaSynBlock);
120     procedure AddFirstSectToTok(dStart: string; TokPos: integer; blk: TFaSynBlock);
CreateBlocknull121     function CreateBlock(blkName: string; showFold: boolean=true;
122       parentBlk: TFaSynBlock=nil): TFaSynBlock;
AddBlocknull123     function AddBlock(dStart, dEnd: string; showFold: boolean=true;
124       parentBlk: TFaSynBlock=nil): TFaSynBlock;
AddSectionnull125     function AddSection(dStart: string; showFold: boolean=true;
126       parentBlk: TFaSynBlock=nil): TFaSynBlock;
AddFirstSectionnull127     function AddFirstSection(dStart: string; showFold: boolean=true;
128       parentBlk: TFaSynBlock=nil): TFaSynBlock;
129     //Funciones para obtener información de bloques
SearchBlocknull130     function SearchBlock(blk: string; out Success: boolean): TFaSynBlock;
NestedBlocksnull131     function NestedBlocks: Integer;
NestedBlocksBeginnull132     function NestedBlocksBegin(LineNumber: integer): Integer;
SearchBeginBlocknull133     function SearchBeginBlock(level: integer; PosY: integer): integer;
SearchEndBlocknull134     function SearchEndBlock(level: integer; PosY: integer): integer;
135     procedure SearchBeginEndBlock(level: integer; PosX, PosY: integer; out
136       pIniBlock, pEndBlock: integer);
TopCodeFoldBlocknull137     function TopCodeFoldBlock(DownIndex: Integer=0): TFaSynBlock;
SetHighlighterAtXYnull138     function SetHighlighterAtXY(XY: TPoint): boolean;
ExploreLinenull139     function ExploreLine(XY: TPoint; out toks: TATokInfo; out CurTok: integer
140       ): boolean;
141     procedure GetBlockInfoAtXY(XY: TPoint; out blk: TFaSynBlock; out level: integer
142       );
GetBlockInfoAtXYnull143     function GetBlockInfoAtXY(XY: TPoint; out blk: TFaSynBlock; out
144       BlockStart: TPoint; out BlockEnd: TPoint): boolean;
145   private   //procesamiento de identificadores especiales
146     //métodos para identificadores especiales
147     procedure metA;
148     procedure metB;
149     procedure metC;
150     procedure metD;
151     procedure metE;
152     procedure metF;
153     procedure metG;
154     procedure metH;
155     procedure metI;
156     procedure metJ;
157     procedure metK;
158     procedure metL;
159     procedure metM;
160     procedure metN;
161     procedure metO;
162     procedure metP;
163     procedure metQ;
164     procedure metR;
165     procedure metS;
166     procedure metT;
167     procedure metU;
168     procedure metV;
169     procedure metW;
170     procedure metX;
171     procedure metY;
172     procedure metZ;
173     procedure metA_;
174     procedure metB_;
175     procedure metC_;
176     procedure metD_;
177     procedure metE_;
178     procedure metF_;
179     procedure metG_;
180     procedure metH_;
181     procedure metI_;
182     procedure metJ_;
183     procedure metK_;
184     procedure metL_;
185     procedure metM_;
186     procedure metN_;
187     procedure metO_;
188     procedure metP_;
189     procedure metQ_;
190     procedure metR_;
191     procedure metS_;
192     procedure metT_;
193     procedure metU_;
194     procedure metV_;
195     procedure metW_;
196     procedure metX_;
197     procedure metY_;
198     procedure metZ_;
199     procedure metUnd;
200     procedure metDol;
201     procedure metArr;
202     procedure metPer;
203     procedure metAmp;
204     procedure metC3;
205   protected   //procesamiento de otros elementos
206     procedure ProcTokenDelim(const d: TTokSpec);
207     procedure metIdentEsp(var mat: TArrayTokSpec);
208     procedure metSimbEsp;
209     //funciones rápidas para la tabla de métodos (símbolos especiales)
210     procedure metSym1Car;
211     //funciones rápidas para la tabla de métodos (tokens delimitados)
212     procedure metUniLin1;
213     procedure metFinLinea;
214     //funciones llamadas en medio de rangos
215     procedure ProcEndLine;
216     procedure ProcRangeEndSym;
217     procedure ProcRangeEndSym1;
218     procedure ProcRangeEndIden;
219   private   //Utilidades para analizador léxico
GetStatenull220     function GetState: TFaLexerState;
221     procedure SetState(state: TFaLexerState);
222   public //Utilidades para analizador léxico
GetXnull223     function GetX: Integer; inline; //devuelve la posición X actual del resaltador
GetYnull224     function GetY: Integer; inline; //devuelve la posición Y actual del resaltador
GetXYnull225     function GetXY: TPoint;  //devuelve la posición actual del resaltador
226     property Range: TPtrTokEspec read fRange write fRange;
227     property State: TFaLexerState read GetState write SetState;
228   public     //métodos OVERRIDE
229     procedure SetLine(const NewValue: String; LineNumber: Integer); override;
230     procedure Next; override;
GetEolnull231     function  GetEol: Boolean; override;
232     procedure GetTokenEx(out TokenStart: PChar; out TokenLength: integer); override;
GetTokenAttributenull233     function  GetTokenAttribute: TSynHighlighterAttributes; override;
GetDefaultAttributenull234     function GetDefaultAttribute(Index: integer): TSynHighlighterAttributes; override;
GetTokennull235     function GetToken: String; override;
GetTokenPosnull236     function GetTokenPos: Integer; override;
GetTokenKindnull237     function GetTokenKind: integer; override;
238     procedure ResetRange; override;
GetRangenull239     function GetRange: Pointer; override;
240     procedure SetRange(Value: Pointer); override;
241     constructor Create(AOwner: TComponent); override;
242     destructor Destroy; override;
243 {//published   //Se crean accesos a las propiedades
244     property SymbolAttri: TSynHighlighterAttributes read fSymbolAttri write fSymbolAttri;
245     property SpaceAttri: TSynHighlighterAttributes read fSpaceAttri write fSpaceAttri;
246     property IdentifierAttri: TSynHighlighterAttributes read fIdentifAttri write fIdentifAttri;
247     property NumberAttri: TSynHighlighterAttributes read fNumberAttri write fNumberAttri;
248     property KeywordAttri: TSynHighlighterAttributes read fKeywordAttri write fKeywordAttri;
249     property StringAttri: TSynHighlighterAttributes read fStringAttri write fStringAttri;
250     property CommentAttri: TSynHighlighterAttributes read fCommentAttri write fCommentAttri;}
251   end;
252 
253 implementation
254 
255 uses
256   Resource;
257 
258 const
259 {
260     ERR_IDENTIF_EMPTY = 'Identificador vacío.';
261     ERR_INVAL_CHAR_IDEN_ = 'Caracter no válido para Identificador: ';
262     ERR_IDENTIF_EXIST = 'Ya existe identificador: ';
263     ERR_EMPTY_SYMBOL = 'Símbolo vacío';
264     ERR_EMPTY_IDENTIF = 'Identificador vacío';
265     ERR_SYMBOL_EXIST = 'Ya existe símbolo.';
266     ERR_MUST_DEF_CHARS = 'Debe indicarse atributo "CharsStart=" en etiqueta <IDENTIFIERS ...>';
267     ERR_MUST_DEF_CONT = 'Debe indicarse atributo "Content=" en etiqueta <IDENTIFIERS ...>';
268     ERR_INVAL_LAB_BLK = 'Etiqueta "%S" no válida para etiqueta <BLOCK ...>';
269     ERR_INVAL_LAB_TOK = 'Invalid label "%s" for <TOKEN ...>';
270     ERR_INVAL_LAB_SEC = 'Etiqueta "%S" no válida para etiqueta <SECTION ...>';
271     ERR_INCOMP_TOK_DEF_ = 'Definición incompleta de token: ';
272     ERR_UNKNOWN_LABEL = 'Etiqueta no reconocida <%s>;
273     ERR_INVAL_LBL_IDEN = 'Etiqueta "%s" no válida para etiqueta <IDENTIFIERS ...>';
274     ERR_INVAL_LBL_IN_LBL = 'Etiqueta "%s" no válida para etiqueta <SYMBOLS ...>';
275     ERR_BLK_NO_DEFINED = 'No se encuentra definido el bloque: ';
276     ERR_MAX_NUM_TOKCON = 'Máximo número de tokens por contenido superado';
277     ERR_UNKNOWN_ATTRIB = 'Atributo "%s" no existe.'
278     ERROR_LOADING_ = 'Error cargando: ';
279   }
280     ERR_IDENTIF_EMPTY = 'Empty identifier.';
281     ERR_INVAL_CHAR_IDEN_ = 'Invalid character for identifier: ';
282     ERR_IDENTIF_EXIST = 'Identifier already exists: ';
283     ERR_EMPTY_SYMBOL = 'Empty Symbol';
284     ERR_EMPTY_IDENTIF = 'Empty Identifier';
285     ERR_SYMBOL_EXIST = 'Symbol already exists.';
286     ERR_MUST_DEF_CHARS = 'It must be indicated "CharsStart=" in label <IDENTIFIERS ...>';
287     ERR_MUST_DEF_CONT = 'It must be indicated "Content=" in label <IDENTIFIERS ...>';
288     ERR_INVAL_LAB_BLK = 'Invalid label "%s" for <BLOCK ...>';
289     ERR_INVAL_LAB_TOK = 'Invalid label "%s" for <TOKEN ...>';
290     ERR_INVAL_LAB_SEC = 'Invalid label "%s" for <SECTION ...>';
291     ERR_INCOMP_TOK_DEF_ = 'Incomplete token definition: ';
292     ERR_UNKNOWN_LABEL = 'Unknown label <%s>';
293     ERR_INVAL_LBL_IDEN = 'Invalid label "%s", for label <IDENTIFIERS ...>';
294     ERR_INVAL_LBL_IN_LBL = 'Invalid label "%s", for label <SYMBOLS ...>';
295     ERR_BLK_NO_DEFINED = 'Undefined block: ';
296     ERR_MAX_NUM_TOKCON = 'Maximun numbers of tokens by Content Added.';
297     ERR_UNKNOWN_ATTRIB = 'Attribute "%s" doesn''t exist.';
298     ERROR_LOADING_ = 'Error loading: ';
299 
300   { TSynFacilSyn }
301 
302 //**************** Funciones de bajo nivel ****************
TSynFacilSyn.CreaBuscIdeEspecnull303 function TSynFacilSyn.CreaBuscIdeEspec(out mat: TPtrATokEspec; cad: string;
304                                        out i:integer; TokPos: integer = 0): boolean;
305 {Busca o crea el identificador especial indicado en "cad". Si ya existe, devuelve
306  TRUE, y actualiza "i" con su posición. Si no existe, crea el token especial y devuelve
307  la referencia en "i". En "mat" devuelve la referencia a la tabla que corresponda al
308  identificador. Puede generar una excepción si el identificador no empieza con un
309  caracter válido}
310 var
311   met: TFaProcMetTable;
312   c: Char;
313 begin
314   Result := false;  //valor por defecto
315   TableIdent(cad, mat, met);  //busca tabla y método (Puede generar excepción)
316   //Verifica si existe
317   if CreaBuscTokEspec(mat^, copy(cad,2,length(cad)), i, TokPos) then begin
318     exit(true);  //Ya existe
319   end;
320   //No existía, pero se creó. Ahora hay que actualizar la tabla de métodos
321   mat^[i].orig:=cad;  //guarda el identificador original
322   c := cad[1]; //primer caracter
323   if CaseSensitive then begin //sensible a la caja
324     fProcTable[c] := met;
325   end else begin
326     fProcTable[LowerCase(c)] := met;
327     fProcTable[UpCase(c)] := met;
328   end;
329 end;
TSynFacilSyn.CreaBuscSymEspecnull330 function TSynFacilSyn.CreaBuscSymEspec(out mat: TPtrATokEspec; cad: string;
331                                        out i:integer; TokPos: integer = 0): boolean;
332 {Busca o crea el símbolo especial indicado en "cad". Si ya existe, devuelve TRUE, y
333  actualiza "i" con su posición. Si no existe, crea el token especial y devuelve la referencia
334  en "i". En "mat" devuelve la referencia a la tabla que corresponda al símbolo (por ahora
335  siempre será mSymb0).}
336 begin
337   Result := false;  //valor por defecto
338   mat := @mSym0;  //no hace falta buscarlo
339   //Verifica si existe
340   if CreaBuscTokEspec(mSym0, cad, i, TokPos) then
341     exit(true);  //Ya existe.
342   //No existía, pero se creó.
343 end;
CreaBuscEspecnull344 function TSynFacilSyn.CreaBuscEspec(out tok: TPtrTokEspec; cad: string;
345                                           TokPos: integer): boolean;
346 {Busca o crea un token especial (identificador o símbolo), con texto "cad" y posición en
347  "TokPos". Si ya existe, devuelve TRUE, y su referencia en "tok". Si no existe, crea el
348  token especial y devuelve su referencia en "tok". Puede generar excepción.}
349 var
350   mat: TPtrATokEspec;
351   i: integer;
352 begin
353   if cad[1] in charsIniIden then begin  //delimitador es identificador
354     Result := CreaBuscIdeEspec(mat, cad, i, TokPos); //busca o crea
355     if not Result then
356       mat^[i].tTok:=tnIdentif;  //es token nuevo, hay que darle atributo por defecto
357   end else begin   //el delimitador inicial es símbolo
358     Result := CreaBuscSymEspec(mat, cad, i, TokPos);  //busca o crea
359     if not Result then
360       mat^[i].tTok:=tnSymbol;  //es token nuevo, hay que darle atributo por defecto
361   end;
362   tok := @mat^[i];   //devuelve referencia a token especial
363 end;
364 
365 procedure TSynFacilSyn.TableIdent(iden: string; out mat: TPtrATokEspec;
366   out met: TFaProcMetTable);
367 {Devuelve una referencia a la tabla que corresponde a un identificador y el método
368 que debe procesarlo. Si no encuentra una tabla apropiada para el identificador
369 (caracter inicial no válido) genera una excepción}
370 var
371   c: char;
372 begin
373   if iden = '' then raise ESynFacilSyn.Create(ERR_IDENTIF_EMPTY);
374   c := iden[1]; //primer caracter
375   mat :=nil; met := nil;   //valores por defecto
376   if CaseSensitive then begin //sensible a la caja
377     case c of
378     'A': begin mat:= @mA;  met := @metA; end;
379     'B': begin mat:= @mB;  met := @metB; end;
380     'C': begin mat:= @mC;  met := @metC; end;
381     'D': begin mat:= @mD;  met := @metD; end;
382     'E': begin mat:= @mE;  met := @metE; end;
383     'F': begin mat:= @mF;  met := @metF; end;
384     'G': begin mat:= @mG;  met := @metG; end;
385     'H': begin mat:= @mH;  met := @metH; end;
386     'I': begin mat:= @mI;  met := @metI; end;
387     'J': begin mat:= @mJ;  met := @metJ; end;
388     'K': begin mat:= @mK;  met := @metK; end;
389     'L': begin mat:= @mL;  met := @metL; end;
390     'M': begin mat:= @mM;  met := @metM; end;
391     'N': begin mat:= @mN;  met := @metN; end;
392     'O': begin mat:= @mO;  met := @metO; end;
393     'P': begin mat:= @mP;  met := @metP; end;
394     'Q': begin mat:= @mQ;  met := @metQ; end;
395     'R': begin mat:= @mR;  met := @metR; end;
396     'S': begin mat:= @mS;  met := @metS; end;
397     'T': begin mat:= @mT;  met := @metT; end;
398     'U': begin mat:= @mU;  met := @metU; end;
399     'V': begin mat:= @mV;  met := @metV; end;
400     'W': begin mat:= @mW;  met := @metW; end;
401     'X': begin mat:= @mX;  met := @metX; end;
402     'Y': begin mat:= @mY;  met := @metY; end;
403     'Z': begin mat:= @mZ;  met := @metZ; end;
404     'a': begin mat:= @mA_; met := @metA_;end;
405     'b': begin mat:= @mB_; met := @metB_;end;
406     'c': begin mat:= @mC_; met := @metC_;end;
407     'd': begin mat:= @mD_; met := @metD_;end;
408     'e': begin mat:= @mE_; met := @metE_;end;
409     'f': begin mat:= @mF_; met := @metF_;end;
410     'g': begin mat:= @mG_; met := @metG_;end;
411     'h': begin mat:= @mH_; met := @metH_;end;
412     'i': begin mat:= @mI_; met := @metI_;end;
413     'j': begin mat:= @mJ_; met := @metJ_;end;
414     'k': begin mat:= @mK_; met := @metK_;end;
415     'l': begin mat:= @mL_; met := @metL_;end;
416     'm': begin mat:= @mM_; met := @metM_;end;
417     'n': begin mat:= @mN_; met := @metN_;end;
418     'o': begin mat:= @mO_; met := @metO_;end;
419     'p': begin mat:= @mP_; met := @metP_;end;
420     'q': begin mat:= @mQ_; met := @metQ_;end;
421     'r': begin mat:= @mR_; met := @metR_;end;
422     's': begin mat:= @mS_; met := @metS_;end;
423     't': begin mat:= @mT_; met := @metT_;end;
424     'u': begin mat:= @mU_; met := @metU_;end;
425     'v': begin mat:= @mV_; met := @metV_;end;
426     'w': begin mat:= @mW_; met := @metW_;end;
427     'x': begin mat:= @mX_; met := @metX_;end;
428     'y': begin mat:= @mY_; met := @metY_;end;
429     'z': begin mat:= @mZ_; met := @metZ_;end;
430     //adicionales
431     '_': begin mat:= @m_  ;met := @metUnd;end;
432     '$': begin mat:= @mDol;met := @metDol;  end;
433     '@': begin mat:= @mArr;met := @metArr;  end;
434     '%': begin mat:= @mPer;met := @metPer;  end;
435     '&': begin mat:= @mAmp;met := @metAmp;  end;
436     end;
437   end else begin  //no es sensible a la caja
438     case c of
439     'A','a': begin mat:= @mA;  met:= @metA;  end;
440     'B','b': begin mat:= @mB;  met:= @metB; end;
441     'C','c': begin mat:= @mC;  met:= @metC; end;
442     'D','d': begin mat:= @mD;  met:= @metD; end;
443     'E','e': begin mat:= @mE;  met:= @metE; end;
444     'F','f': begin mat:= @mF;  met:= @metF; end;
445     'G','g': begin mat:= @mG;  met:= @metG; end;
446     'H','h': begin mat:= @mH;  met:= @metH; end;
447     'I','i': begin mat:= @mI;  met:= @metI; end;
448     'J','j': begin mat:= @mJ;  met:= @metJ; end;
449     'K','k': begin mat:= @mK;  met:= @metK; end;
450     'L','l': begin mat:= @mL;  met:= @metL; end;
451     'M','m': begin mat:= @mM;  met:= @metM; end;
452     'N','n': begin mat:= @mN;  met:= @metN; end;
453     'O','o': begin mat:= @mO;  met:= @metO; end;
454     'P','p': begin mat:= @mP;  met:= @metP; end;
455     'Q','q': begin mat:= @mQ;  met:= @metQ; end;
456     'R','r': begin mat:= @mR;  met:= @metR; end;
457     'S','s': begin mat:= @mS;  met:= @metS; end;
458     'T','t': begin mat:= @mT;  met:= @metT; end;
459     'U','u': begin mat:= @mU;  met:= @metU; end;
460     'V','v': begin mat:= @mV;  met:= @metV; end;
461     'W','w': begin mat:= @mW;  met:= @metW; end;
462     'X','x': begin mat:= @mX;  met:= @metX; end;
463     'Y','y': begin mat:= @mY;  met:= @metY; end;
464     'Z','z': begin mat:= @mZ;  met:= @metZ; end;
465     '_'    : begin mat:= @m_  ;met:= @metUnd;end;
466     '$'    : begin mat:= @mDol;met:= @metDol;end;
467     '@'    : begin mat:= @mArr;met:= @metArr;end;
468     '%'    : begin mat:= @mPer;met:= @metPer;end;
469     '&'    : begin mat:= @mAmp;met:= @metAmp;end;
470     #$C3   : begin mat:= @mC3; met:= @metC3; end;  //página 195 de UTF-8
471     end;
472   end;
473   //verifica error
474   if mat = nil then begin
475     raise ESynFacilSyn.Create(ERR_INVAL_CHAR_IDEN_+iden);
476   end;
477 end;
478 procedure TSynFacilSyn.SetTokContent(tc: tFaTokContent; dStart: string;
479    TypDelim: TFaTypeDelim; typToken: integer);
480 //Configura la definición de un token por contenido. De ser así devuelve TRUE y
481 //actualiza la tabla de métodos con el método indicado. Puede generar excepción.
482 var
483   tmp: string;
484   tok: TPtrTokEspec;
485 begin
486   ValidateParamStart(dStart, lisTmp);   //Valida parámetro "dStart", y devuelve en lista.
487   tc.TokTyp:= typToken;  //atributo inicial
488   tc.CaseSensitive := CaseSensitive;  //toma el mismo comportamiento de caja
489   /////// Configura detección de inicio
490   {Si es rango de caracteres, agrega cada caracter como símbolo especial, aunque parezca
491   ineficiente. Pero de esta forma se podrán procesar tokens por contenido que empiecen
492   con el mismo caracter. Además, de ser posible, la función Rebuild() optimizará luego
493   el procesamiento.}
494   for tmp in lisTmp do begin
495     CreaBuscEspec(tok, tmp, 0);  //busca o crea
496     //actualiza sus campos. Cambia, si ya existía
497     tok^.tTok:=typToken;   //no se espera usar este campo, sino  "tc.TokTyp"
498     tok^.typDel:=TypDelim;  //solo es necesario marcarlo como que es por contenido
499   end;
500 end;
501 procedure TSynFacilSyn.ClearMethodTables;
502 {Limpia la tabla de métodos, usada para identificar a los tokens de la sintaxis.
503  También limpia las definiciones de tokens por contenido.
504  Proporciona una forma rápida de identificar la categoría de token.}
505 var i: Char;
506 begin
507   lisBlocks.Clear;   //inicia lista de bloques
508   nTokenCon := 0;    //inicia contador de tokens por contenido
509   tc1.Clear;
510   tc2.Clear;
511   tc3.Clear;
512   tc4.Clear;
513   for i := #0 to #255 do
514     case i of
515       //caracteres blancos, son fijos
516       #1..#32 : fProcTable[i] := @metSpace;
517       //fin de línea
518       #0      : fProcTable[i] := @metNull;   //Se lee el caracter de marca de fin de cadena
519       else //los otros caracteres (alfanuméricos o no)
520         fProcTable[i] := @metSymbol;  //se consideran símbolos
521     end;
522 end;
523 //definición de tokens por contenido
DefTokContentnull524 function TSynFacilSyn.DefTokContent(dStart: string;
525   typToken: integer): tFaTokContent;
526 {Crea un token por contenido, y devuelve una referencia al token especial agregado.
527 Se debe haber limpiado previamente la tabla de métodos con "ClearMethodTables"
528 Solo se permite definir hasta 4 tokens por contenido. Puede generar excepción}
529 begin
530   if nTokenCon = 0 then begin       //está libre el 1
531     SetTokContent(tc1, dStart, tdConten1, typToken);
532     Result := tc1;   //devuelve referencia
533     inc(nTokenCon);
534   end else if nTokenCon = 1 then begin //está libre el 2
535     SetTokContent(tc2, dStart, tdConten2, typToken);
536     Result := tc2;   //devuelve referencia
537     inc(nTokenCon);
538   end else if nTokenCon = 2 then begin //está libre el 3
539     SetTokContent(tc3, dStart, tdConten3, typToken);
540     Result := tc3;   //devuelve referencia
541     inc(nTokenCon);
542   end else if nTokenCon = 3 then begin //está libre el 4
543     SetTokContent(tc4, dStart, tdConten4, typToken);
544     Result := tc4;   //devuelve referencia
545     inc(nTokenCon);
546   end else begin //las demás declaraciones, generan error
547     raise ESynFacilSyn.Create(ERR_MAX_NUM_TOKCON);
548   end;
549 end;
550 procedure TSynFacilSyn.DefTokContent(dStart, Content: string;
551   typToken: integer; Complete:boolean = false);
552 {Versión simplificada para crear tokens por contenido sencillos. El parámetro
553 "Content", se debe ingresar com expresión regular. Un ejemplo sencillo sería:
554   hlt.DefTokContent('[0-9]','[0-9]*');
555 Se debe haber limpiado previamente la tabla de métodos con "ClearMethodTables"
556 Solo se permite definir hasta 4 tokens}
557 var
558   p: tFaTokContent;
559 begin
560   p := DefTokContent(dStart, typToken);
561   p.AddRegEx(Content, Complete);   //agrega contenido como expresión regular
562 end;
563 //manejo de identificadores y símbolos especiales
564 procedure TSynFacilSyn.ClearSpecials;
565 //Limpia la lista de identificadores especiales y de símbolos delimitadores.
566 begin
567   //ídentificadores
568   SetLength(mA,0); SetLength(mB,0); SetLength(mC,0); SetLength(mD,0);
569   SetLength(mE,0); SetLength(mF,0); SetLength(mG,0); SetLength(mH,0);
570   SetLength(mI,0); SetLength(mJ,0); SetLength(mK,0); SetLength(mL,0);
571   SetLength(mM,0); SetLength(mN,0); SetLength(mO,0); SetLength(mP,0);
572   SetLength(mQ,0); SetLength(mR,0); SetLength(mS,0); SetLength(mT,0);
573   SetLength(mU,0); SetLength(mV,0); SetLength(mW,0); SetLength(mX,0);
574   SetLength(mY,0); SetLength(mZ,0);
575   SetLength(mA_,0); SetLength(mB_,0); SetLength(mC_,0); SetLength(mD_,0);
576   SetLength(mE_,0); SetLength(mF_,0); SetLength(mG_,0); SetLength(mH_,0);
577   SetLength(mI_,0); SetLength(mJ_,0); SetLength(mK_,0); SetLength(mL_,0);
578   SetLength(mM_,0); SetLength(mN_,0); SetLength(mO_,0); SetLength(mP_,0);
579   SetLength(mQ_,0); SetLength(mR_,0); SetLength(mS_,0); SetLength(mT_,0);
580   SetLength(mU_,0); SetLength(mV_,0); SetLength(mW_,0); SetLength(mX_,0);
581   SetLength(mY_,0); SetLength(mZ_,0);
582   SetLength(m_,0); SetLength(mDol,0); SetLength(mArr,0);
583   SetLength(mPer,0); SetLength(mAmp,0); SetLength(mC3,0);
584   //símbolos
585   SetLength(mSym,0);
586   SetLength(mSym0,0);  //limpia espacio temporal
587 end;
588 procedure TSynFacilSyn.AddIdentSpec(iden: string; tokTyp: integer;
589   TokPos: integer);
590 //Método público para agregar un identificador especial cualquiera.
591 //Si el identificador no inicia con caracter válido, o ya existe, genera una excepción.
592 var i: integer;
593     mat: TPtrATokEspec;
594 begin
595   if iden = '' then raise ESynFacilSyn.Create(ERR_EMPTY_IDENTIF);
596   //Verifica si existe
597   if CreaBuscIdeEspec(mat, iden, i, TokPos) then begin  //puede generar excepción
598     //Genera error, porque el identif. ya existe
599     raise ESynFacilSyn.Create(ERR_IDENTIF_EXIST+iden);
600   end;
601   //se ha creado uno nuevo
602   mat^[i].tTok:=tokTyp;  //solo cambia atributo
603 end;
604 procedure TSynFacilSyn.AddIdentSpecList(listIden: string; tokTyp: integer;
605   TokPos: integer);
606 //Permite agregar una lista de identificadores especiales separados por espacios.
607 //Puede gernerar excepción, si algún identificador está duplicado o es erróneo.
608 var
609   iden   : string;
610   i      : integer;
611 begin
612   //Carga identificadores
613   lisTmp.Clear;
614   lisTmp.Delimiter := ' ';
615   //StringReplace(listIden, #13#10, ' ',[rfReplaceAll]);
616   lisTmp.DelimitedText := listIden;
617   for i:= 0 to lisTmp.Count -1 do
618     begin
619       iden := trim(lisTmp[i]);
620       if iden = '' then continue;
621       AddIdentSpec(iden, tokTyp, TokPos);  //puede generar excepción
622     end;
623 end;
624 procedure TSynFacilSyn.AddKeyword(iden: string);
625 //Método público que agrega un identificador "Keyword" a la sintaxis
626 //Si el identificador es erróneso (caracter inicial no válido) o ya existe, genera
627 //una excepción.
628 begin
629   AddIdentSpec(iden, tnKeyword);
630 end;
631 procedure TSynFacilSyn.AddSymbSpec(symb: string; tokTyp: integer;
632   TokPos: integer);
633 //Método público para agregar un símbolo especial cualquiera.
634 //Si el símbolo ya existe, genera una excepción.
635 var i: integer;
636     mat: TPtrATokEspec;
637 begin
638   if symb = '' then raise ESynFacilSyn.Create(ERR_EMPTY_SYMBOL);
639   //Verifica si existe
640   if CreaBuscSymEspec(mat, symb, i, TokPos) then begin //busca o crea
641     //Genera error, porque el símbolo. ya existe
642     raise ESynFacilSyn.Create(ERR_SYMBOL_EXIST);
643   end;
644   //se ha creado uno nuevo
645   mat^[i].tTok:=tokTyp;  //solo cambia atributo
646 end;
647 procedure TSynFacilSyn.AddSymbSpecList(listSym: string; tokTyp: integer;
648   TokPos: integer);
649 //Permite agregar una lista de símbolos especiales separados por espacios.
650 //Puede gernerar excepción, si algún símbolo está duplicado.
651 var
652   iden   : string;
653   i      : integer;
654 begin
655   //Carga identificadores
656   lisTmp.Clear;
657   lisTmp.Delimiter := ' ';
658   //StringReplace(listSym, #13#10, ' ',[rfReplaceAll]);
659   lisTmp.DelimitedText := listSym;
660   for i:= 0 to lisTmp.Count -1 do
661     begin
662       iden := trim(lisTmp[i]);
663       if iden = '' then continue;
664       AddSymbSpec(iden, tokTyp, TokPos);  //puede generar excepción
665     end;
666 end;
667 //definición de tokens delimitados
668 procedure TSynFacilSyn.DefTokDelim(dStart, dEnd: string; tokTyp: integer;
669   tipDel: TFaTypeDelim; havFolding: boolean; chrEscape: char);
670 {Función genérica para agregar un token delimitado a la sintaxis. Si encuentra error,
671 genera una excepción}
672 var
673   tok  : TPtrTokEspec;
674   tmp, tmpnew: String;
675   procedure ActProcRange(var r: TTokSpec);
676   //Configura el puntero pRange() para la función apropiada de acuerdo al delimitador final.
677   begin
678     if r.typDel = tdNull then begin  //no es delimitador
679       r.pRange:=nil;
680       exit;
681     end;
682     if r.dEnd = '' then exit;
683     if r.dEnd = #13 then begin   //Como comentario de una línea
684       //no puede ser multilínea
685       r.pRange := @ProcEndLine;
686       exit;
687     end;
688     //los siguientes casos pueden ser multilínea
689     if r.dEnd[1] in charsIniIden then begin //es identificador
690       r.pRange:=@ProcRangeEndIden;
691     end else begin  //es símbolo
692       if length(r.dEnd) = 1 then begin
693         r.pRange:=@ProcRangeEndSym1;  //es más óptimo
694       end else begin
695         r.pRange:=@ProcRangeEndSym;
696       end;
697     end;
698   end;
699 begin
700   if dEnd='' then dEnd := #13;  //no se permite delimitador final vacío
701   ValidateParamStart(dStart, lisTmp);
702   dEnd := ReplaceEscape(dEnd);  //convierte secuencias de escape
703   VerifDelim(dEnd);
704   //configura token especial
705   for tmp in lisTmp do begin
706     if (tmp<>'') and (tmp[1]='^') then begin
707       tmpnew := copy(tmp,2,length(tmp));
708       CreaBuscEspec(tok, tmpnew, 1); //busca o crea
709     end else begin
710       if copy(tmp,1,2) = '\^' then begin  //caracter escapado
711         tmpnew := '^' + copy(tmp,3,length(tmp));
712         CreaBuscEspec(tok, tmpnew, 0); //busca o crea
713       end else begin
714         CreaBuscEspec(tok, tmp, 0); //busca o crea
715       end;
716     end;
717     //actualiza sus campos. Cambia, si ya existía
718     tok^.dEnd  :=dEnd;
719     tok^.typDel:=tipDel;
720     tok^.tTok  :=tokTyp;
721     tok^.folTok:=havFolding;
722     tok^.chrEsc:=chrEscape;
723     ActProcRange(tok^);  //completa .pRange()
724   end;
725 end;
726 procedure TSynFacilSyn.RebuildSymbols;
727 {Crea entradas en la tabla de métodos para procesar los caracteres iniciales de los
728  símbolos especiales. Así se asegura que se detectarán siempre.
729  Se debe llamar después de haber agregado los símbolos especiales a mSym[]}
730 var
731   i,j   : integer;
732   maximo: integer;
733   aux   : TTokSpec;
734   c     : char;
735 begin
736   {Ordena mSym[], poniendo los de mayor tamaño al inicio, para que la búsqueda considere
737    primero a los símbolos de mayor tamaño}
738   maximo := High(mSym);
739   for i:=0 to maximo-1 do
740     for j:=i+1 to maximo do begin
741       if (length(mSym[i].txt) < length(mSym[j].txt)) then begin
742         aux:=mSym[i];
743         mSym[i]:=mSym[j];
744         mSym[j]:=aux;
745       end;
746     end;
747   //muestra los símbolos especiales que existen
748   {$IFDEF DebugMode}
749   DebugLn('------ delimitadores símbolo, ordenados --------');
750   for tokCtl in mSym do DebugLn('  delim: '+ tokCtl.cad );
751   DebugLn('---------actualizando tabla de funciones----------');
752   {$ENDIF}
753   {Captura los caracteres válidos para delimitadores y asigna las funciones
754    para el procesamiento de delimitadores, usando el símbolo inicial}
755   i := 0;
756   while i <= High(mSym) do begin
757     c := mSym[i].txt[1];   //toma primer caracter
758     if fProcTable[c] <> @metSimbEsp then begin  //prepara procesamiento de delimitador
759       {$IFDEF DebugMode}
760       DebugLn('  puntero a funcion en: [' + c + '] -> @metSimbEsp');
761       {$ENDIF}
762       fProcTable[c] := @metSimbEsp;  //prepara procesamiento de delimitador
763     end;
764     { Para hacerlo más óptimo se debería usar una matriz para cada símbolo, de
765      la misma forma a como se hace con los identificadores.}
766     inc(i);
767   end;
768 end;
769 procedure TSynFacilSyn.FirstXMLExplor(doc: TXMLDocument);
770 {Hace la primera exploración al archivo XML, para procesar la definición de Symbolos
771  e Identificadores. Si encuentra algún error, genera una excepción.
772  Si no encuentra definición de Identificadores, crea una definición por defecto}
773 var
774   nodo, atri   : TDOMNode;
775   i,j            : integer;
776   nombre         : string;
777   defIDENTIF     : boolean;  //bandera
778   tipTok         : integer;
779   tExt, tName, tCasSen, tColBlk: TFaXMLatrib;
780   tCharsStart, tContent, tAtrib: TFaXMLatrib;
781   tTokPos: TFaXMLatrib;
782 begin
783   defIDENTIF := false;
784 //  defSIMBOLO := false;
785   //////////// explora atributos del lenguaje//////////
786   tExt  := ReadXMLParam(doc.DocumentElement, 'Ext');
787   tName := ReadXMLParam(doc.DocumentElement, 'Name');
788   tCasSen :=ReadXMLParam(doc.DocumentElement, 'CaseSensitive');
789   tColBlk :=ReadXMLParam(doc.DocumentElement, 'ColorBlock');
790   //carga atributos leidos
791   CheckXMLParams(doc.DocumentElement, 'Ext Name CaseSensitive ColorBlock');
792   LangName := tName.val;
793   Extensions := tExt.val;
794   CaseSensitive := tCasSen.bol;
795   case UpCase(tColBlk.val) of  //coloreado de bloque
796   'LEVEL': ColBlock := cbLevel;
797   'BLOCK': ColBlock := cbBlock;
798   else ColBlock:= cbNull;
799   end;
800 
801   ////////////// explora nodos ////////////
802   for i:= 0 to doc.DocumentElement.ChildNodes.Count - 1 do begin
803      // Lee un Nodo o Registro
804      nodo := doc.DocumentElement.ChildNodes[i];
805      nombre := UpCase(AnsiString(nodo.NodeName));
806      if nombre = 'IDENTIFIERS' then begin
807        defIDENTIF := true;      //hay definición de identificadores
808        ////////// Lee parámetros //////////
809        tCharsStart  := ReadXMLParam(nodo,'CharsStart');
810        tContent:= ReadXMLParam(nodo,'Content');
811        CheckXMLParams(nodo, 'CharsStart Content'); //valida
812        ////////// verifica los atributos indicados
813        if tCharsStart.hay and tContent.hay then  //lo normal
814          DefTokIdentif(ToListRegex(tCharsStart), ToListRegex(tContent)+'*')   //Fija caracteres
815        else if not tCharsStart.hay and not tContent.hay then  //etiqueta vacía
816          DefTokIdentif('[A-Za-z$_]', '[A-Za-z0-9_]*')  //def. por defecto
817        else if not tCharsStart.hay  then
818          raise ESynFacilSyn.Create(ERR_MUST_DEF_CHARS)
819        else if not tContent.hay  then
820          raise ESynFacilSyn.Create(ERR_MUST_DEF_CONT);
821        ////////// explora nodos hijos //////////
822        for j := 0 to nodo.ChildNodes.Count-1 do begin
823          atri := nodo.ChildNodes[j];
824          nombre := UpCase(AnsiString(atri.NodeName));
825          if nombre = 'TOKEN' then begin  //definición completa
826            //lee atributos
827            tAtrib:= ReadXMLParam(atri,'Attribute');
828            tTokPos:= ReadXMLParam(atri,'TokPos');  //posición de token
829            CheckXMLParams(atri, 'Attribute TokPos'); //valida
830            tipTok := GetAttribIDByName(tAtrib.val);
831            if tipTok = -1 then begin
832              raise ESynFacilSyn.Create(Format(ERR_UNKNOWN_ATTRIB, [tAtrib.val]));
833            end;
834            //crea los identificadores especiales
835            AddIdentSpecList(AnsiString(atri.TextContent), tipTok, tTokPos.n);
836          end else if IsAttributeName(nombre) then begin  //definición simplificada
837            //lee atributos
838            tTokPos:= ReadXMLParam(atri,'TokPos');  //posición de token
839            CheckXMLParams(atri, 'TokPos'); //valida
840            //Crea los identificadores especiales
841            AddIdentSpecList(AnsiString(atri.TextContent), GetAttribIDByName(nombre), tTokPos.n);
842          end else if nombre = '#COMMENT' then begin
843            //solo para evitar que de mensaje de error
844          end else begin
845            raise ESynFacilSyn.Create(Format(ERR_INVAL_LBL_IDEN, [atri.NodeName]));
846          end;
847        end;
848      end else if nombre = 'SYMBOLS' then begin
849 //       defSIMBOLO := true;      //hay definición de símbolos
850        ////////// Lee atributos, pero no se usan. Es solo protocolar.
851        tCharsStart:= ReadXMLParam(nodo,'CharsStart');
852        tContent   := ReadXMLParam(nodo,'Content');
853        ////////// explora nodos hijos //////////
854        for j := 0 to nodo.ChildNodes.Count-1 do begin
855          atri := nodo.ChildNodes[j];
856          nombre := UpCase(AnsiString(atri.NodeName));
857          if nombre = 'TOKEN' then begin  //definición completa
858            //lee atributos
859            tAtrib := ReadXMLParam(atri,'Attribute');
860            tTokPos:= ReadXMLParam(atri,'TokPos');  //posición de token
861            CheckXMLParams(atri, 'Attribute TokPos'); //valida
862            tipTok := GetAttribIDByName(tAtrib.val);
863            //crea los símbolos especiales
864            AddSymbSpecList(AnsiString(atri.TextContent), tipTok, tTokPos.n);
865          end else if IsAttributeName(nombre) then begin  //definición simplificada
866            //lee atributos
867            tTokPos:= ReadXMLParam(atri,'TokPos');  //posición de token
868            CheckXMLParams(atri, 'TokPos'); //valida
869            //crea los símbolos especiales
870            AddSymbSpecList(AnsiString(atri.TextContent), GetAttribIDByName(nombre), tTokPos.n);
871          end else if nombre = '#COMMENT' then begin
872            //solo para evitar que de mensaje de error
873          end else begin
874            raise ESynFacilSyn.Create(Format(ERR_INVAL_LBL_IN_LBL, [atri.NodeName]));
875          end;
876        end;
877      end else if nombre = 'SAMPLE' then begin  //Cödigo de muestra
878        fSampleSource := AnsiString(nodo.TextContent);  //Carga texto
879      end else if ProcXMLattribute(nodo) then begin
880        //No es necesario hacer nada
881      end;
882      //ignora las otras etiquetas, en esta pasada.
883   end;
884   //verifica configuraciones por defecto
885   if not defIDENTIF then //no se indicó etiqueta IDENTIFIERS
886     DefTokIdentif('[A-Za-z$_]', '[A-Za-z0-9_]*');  //def. por defecto
887 //  if not defSIMBOLO then //no se indicó etiqueta SYMBOLS
888 end;
889 procedure TSynFacilSyn.LoadFromStream(Stream: TStream);
890 //Carga una sintaxis desde archivo
891 var
892   doc     : TXMLDocument;
893   nodo    : TDOMNode;
894   i, j    : integer;
895   nombre  : string;
896   subExp     : string;
897   p : tFaTokContent;
898   t : tFaRegExpType;
899   dStart: String;
900   tipTok  : integer;
901   tStart, tEnd, tContent, tAtrib : TFaXMLatrib;
902   tRegex, tCharsStart, tMultiline, tFolding : TFaXMLatrib;
903   tMatch: TFaXMLatrib;
904   match: Boolean;
905   nodo2: TDOMNode;
906   tIfTrue,tIfFalse, tText: TFaXMLatrib;
907   list: String;
908   tEscape,tAtTrue,tAtFalse: TFaXMLatrib;
909   chrEscape: Char;
910 begin
911   {$IFDEF DebugMode}
912   DebugLn('');
913   DebugLn(' === Cargando archivo de sintaxis ===');
914   {$ENDIF}
915   ClearSpecials;     //limpia tablas de identif. y simbolos especiales
916   CreateAttributes;  //Limpia todos los atributos y crea los predefinidos.
917   ClearMethodTables; //Limpia tabla de caracter inicial, y los bloques
918   try
919     ReadXMLFile(doc, Stream);  //carga archivo de lenguaje
920     ////////Primera exploración para capturar elementos básicos de la sintaxis/////////
921     FirstXMLExplor(doc);  //Hace la primera exploración. Puede generar excepción.
922     ///////////Segunda exploración para capturar elementos complementarios///////////
923     //inicia exploración
924     for i:= 0 to doc.DocumentElement.ChildNodes.Count - 1 do begin
925        // Lee un Nodo o Registro
926        nodo := doc.DocumentElement.ChildNodes[i];
927        nombre := UpCase(AnsiString(nodo.NodeName));
928        if (nombre = 'IDENTIFIERS') or (nombre = 'SYMBOLS') or
929           (nombre = 'ATTRIBUTE') or (nombre = 'COMPLETION') or
930           (nombre = 'SAMPLE') or(nombre = '#COMMENT') then begin
931          //No se hace nada. Solo se incluye para evitar error de "etiqueta desconocida"
932 //     end else if IsAttributeName(nombre)  then begin
933        end else if nombre =  'KEYWORD' then begin
934          //forma corta de <TOKEN ATTRIBUTE='KEYWORD'> lista </TOKEN>
935          AddIdentSpecList(AnsiString(nodo.TextContent), tnKeyword);  //Carga Keywords
936        end else if (nombre = 'TOKEN') or
937                    (nombre = 'COMMENT') or (nombre = 'STRING') then begin
938          //Lee atributo
939          if nombre = 'TOKEN' then begin   //Es definición formal de token
940            tAtrib    := ReadXMLParam(nodo,'Attribute');
941            tipTok := GetAttribIDByName(tAtrib.val);
942            if tipTok = -1 then begin
943              raise ESynFacilSyn.Create(Format(ERR_UNKNOWN_ATTRIB, [tAtrib.val]));
944            end;
945          end else begin   //Es definición simplificada
946            tipTok := GetAttribIDByName(nombre);
947            if tipTok = -1 then begin
948              raise ESynFacilSyn.Create(Format(ERR_UNKNOWN_ATTRIB, [nombre]));
949            end;
950          end;
951          //Lee los otros parámetros
952          tStart    := ReadXMLParam(nodo,'Start');
953          tEnd      := ReadXMLParam(nodo,'End');
954          tCharsStart:= ReadXMLParam(nodo,'CharsStart');
955          tContent  := ReadXMLParam(nodo,'Content');
956          tRegex    := ReadXMLParam(nodo,'Regex');
957          tMultiline:=ReadXMLParam(nodo,'Multiline');  //Falso, si no existe
958          tFolding  := ReadXMLParam(nodo,'Folding');    //Falso, si no existe
959          tMatch    := ReadXMLParam(nodo,'RegexMatch'); //Tipo de coincidencia
960          tEscape   := ReadXMLParam(nodo,'Escape'); //
961          if (nombre = 'COMMENT') and not tEnd.hay then tEnd.hay := true; //por compatibilidad
962          //verifica tipo de definición
963          if tContent.hay then begin //Si hay "Content", es token por contenido
964            CheckXMLParams(nodo, 'Start CharsStart Content Attribute');
965            dStart := dStartRegex(tStart, tCharsStart);  //extrae delimitador inicial
966            p := DefTokContent(dStart, tipTok);
967            //define contenido
968            p.AddInstruct(ToListRegex(tContent)+'*','','');
969          end else if tRegex.hay then begin //definición de token por contenido con Regex
970            CheckXMLParams(nodo, 'Start CharsStart Regex Attribute RegexMatch');
971            match := UpCase(tMatch.val)='COMPLETE';
972            if tStart.hay or tCharsStart.hay then begin //modo con delimitador
973              dStart := dStartRegex(tStart, tCharsStart);  //extrae delimitador inicial
974              p := DefTokContent(dStart, tipTok);
975              p.AddRegEx(tRegex.val, match);  //agrega la otra parte de la expresión
976            end else begin  //modo simplificado: <token regex="" />
977              subExp := ExtractRegExpN(tRegex.val, t);  //extrae primera expresión
978              if t = tregChars1_ then begin  //[A-Z]+
979                //Esta forma, normalmente no sería válida, pero se puede dividir
980                //en las formas [A-Z][A-Z]*, y así sería válida
981                list := copy(subExp, 1, length(subExp)-1);  //quita "+"
982                subExp := list;  //transforma en lista simple
983                tRegex.val := list + '*' + tRegex.val;  //completa
984              end;
985              p := DefTokContent(subExp, tipTok);
986              p.AddRegEx(tRegex.val, match);  //agrega la otra parte de la expresión
987            end;
988          end else if tEnd.hay then begin //definición de token delimitado
989            CheckXMLParams(nodo, 'Start CharsStart End Attribute Multiline Folding Escape');
990            dStart := dStartRegex(tStart, tCharsStart);  //extrae delimitador inicial
991            if (tEscape.hay) and (tEscape.val<>'') then chrEscape:= tEscape.val[1]
992            else chrEscape := #0;
993            //no se espera que DefTokDelim(), genere error aquí.
994            if tMultiline.bol then  //es multilínea
995              DefTokDelim(dStart, tEnd.val, tipTok, tdMulLin, tFolding.bol, chrEscape)
996            else  //es de una sola líneas
997              DefTokDelim(dStart, tEnd.val, tipTok, tdUniLin, tFolding.bol, chrEscape);
998          end else begin  //definición incompleta
999            if tStart.hay or tCharsStart.hay then begin //se ha indicado delimitador inicial
1000              dStart := dStartRegex(tStart, tCharsStart);  //extrae delimitador
1001              p := DefTokContent(dStart, tipTok);  //crea un token por contenido
1002              //Hasta aquí se creó un token por contenido. Explora sub-nodos
1003              for j := 0 to nodo.ChildNodes.Count-1 do begin
1004                nodo2 := nodo.ChildNodes[j];
1005                if UpCAse(nodo2.NodeName)='REGEX' then begin  //instrucción
1006                  tText   := ReadXMLParam(nodo2,'Text');
1007                  tIfTrue := ReadXMLParam(nodo2,'IfTrue');
1008                  tIfFalse:= ReadXMLParam(nodo2,'IfFalse');
1009                  tAtTrue := ReadXMLParam(nodo2,'AtTrue');
1010                  tAtFalse:= ReadXMLParam(nodo2,'AtFalse');
1011                  CheckXMLParams(nodo2, 'Text IfTrue IfFalse AtTrue AtFalse');
1012                  //Agrega la instrucción
1013                  p.AddInstruct(tText.val, tIfTrue.val, tIfFalse.val,
1014                        GetAttribIDByName(tAtTrue.val), GetAttribIDByName(tAtFalse.val));
1015                end else if UpCase(nodo2.NodeName)='#COMMENT' then begin
1016                  //solo lo deja pasar,para no generar error
1017                end else begin
1018                  raise ESynFacilSyn.Create(Format(ERR_INVAL_LAB_TOK,[nodo2.NodeName]));
1019                end;
1020              end;
1021            end else begin
1022              raise ESynFacilSyn.Create(ERR_INCOMP_TOK_DEF_ + '<' + nombre + '...');
1023            end;
1024          end;
1025        end else if ProcXMLBlock(nodo, nil) then begin  //bloques válidos en cualquier parte
1026          //No es necesario hacer nada
1027        end else if ProcXMLSection(nodo, nil) then begin //secciones en "MAIN"
1028          //No es necesario hacer nada
1029        end else begin
1030           raise ESynFacilSyn.Create(Format(ERR_UNKNOWN_LABEL,[nombre]));
1031        end;
1032     end;
1033     Rebuild;  //prepara al resaltador
1034     doc.Free;  //libera
1035   except
1036     on e: Exception do begin
1037       //completa el mensaje
1038       e.Message:=ERROR_LOADING_ + 'stream' + #13#10 + e.Message;
1039       doc.Free;
1040       raise   //genera de nuevo
1041     end;
1042   end;
1043 end;
1044 procedure TSynFacilSyn.LoadFromResourceName(Instance: THandle; const ResName: String);
1045 var
1046   rs: TResourceStream;
1047 begin
1048   rs := TResourceStream.Create(Instance, ResName, PChar(RT_RCDATA));
1049   try
1050     rs.Position := 0;
1051     LoadFromStream(rs);
1052   finally
1053     rs.Free;
1054   end;
1055 end;
TSynFacilSyn.ProcXMLBlocknull1056 function TSynFacilSyn.ProcXMLBlock(nodo: TDOMNode; blqPad: TFaSynBlock): boolean;
1057 //Verifica si el nodo tiene la etiqueta <BLOCK>. De ser así, devuelve TRUE y lo procesa.
1058 //Si encuentra error, genera una excepción.
1059 var
1060   i: integer;
1061   tStart, tFolding, tName, tParent : TFaXMLatrib;
1062   tBackCol, tTokPos: TFaXMLatrib;
1063   blq : TFaSynBlock;
1064   nodo2  : TDOMNode;
1065   Success: boolean;
1066   tEnd: TFaXMLatrib;
1067   tCloParnt: TFaXMLatrib;
1068 begin
1069   if UpCase(nodo.NodeName) <> 'BLOCK' then exit(false);
1070   Result := true;  //encontró
1071   //Lee atributos
1072   tStart    := ReadXMLParam(nodo,'Start');
1073   tEnd      := ReadXMLParam(nodo,'End');
1074   tName     := ReadXMLParam(nodo,'Name');
1075   tFolding  := ReadXMLParam(nodo,'Folding');    //Falso, si no existe
1076   tParent   := ReadXMLParam(nodo,'Parent');
1077   tBackCol  := ReadXMLParam(nodo,'BackCol');
1078   tCloParnt := ReadXMLParam(nodo,'CloseParent');
1079   //validaciones
1080   if not tFolding.hay then tFolding.bol:=true;  //por defecto
1081   if not tName.hay then tName.val:='Blk'+IntToStr(lisBlocks.Count+1);
1082   CheckXMLParams(nodo, 'Start End Name Folding Parent BackCol CloseParent');
1083   if tParent.hay then begin //se especificó blqPad padre
1084     blqPad := SearchBlock(tParent.val, Success);  //ubica blqPad
1085     if not Success then raise ESynFacilSyn.Create(ERR_BLK_NO_DEFINED + tParent.val);
1086   end;
1087   //crea el blqoue, con el bloque padre indicado, o el que viene en el parámetro
1088   blq := CreateBlock(tName.val, tFolding.bol, blqPad);
1089   if tStart.hay then AddIniBlockToTok(tStart.val, 0, blq);
1090   if tEnd.hay   then AddFinBlockToTok(tEnd.val, 0, blq);
1091   if tBackCol.hay then begin //lee color
1092     if UpCase(tBackCol.val)='TRANSPARENT' then blq.BackCol:= COL_TRANSPAR
1093     else blq.BackCol:= tBackCol.col;
1094   end;
1095   if tCloParnt.hay then begin
1096     blq.CloseParent:=tCloParnt.bol;
1097   end;
1098   ////////// explora nodos hijos //////////
1099   for i := 0 to nodo.ChildNodes.Count-1 do begin
1100     nodo2 := nodo.ChildNodes[i];
1101     if UpCAse(nodo2.NodeName)='START' then begin  //definición alternativa de delimitador
1102       tTokPos := ReadXMLParam(nodo2,'TokPos');
1103       CheckXMLParams(nodo2, 'TokPos');
1104       //agrega la referecnia del bloque al nuevo token delimitador
1105       AddIniBlockToTok(trim(AnsiString(nodo2.TextContent)), tTokPos.n, blq);
1106     end else if UpCAse(nodo2.NodeName)='END' then begin  //definición alternativa de delimitador
1107       tTokPos := ReadXMLParam(nodo2,'TokPos');
1108       CheckXMLParams(nodo2, 'TokPos');
1109       //agrega la referecnia del bloque al nuevo token delimitador
1110       AddFinBlockToTok(trim(AnsiString(nodo2.TextContent)), tTokPos.n, blq);
1111     end else if ProcXMLSection(nodo2, blq) then begin  //definición de sección
1112       //No es necesario procesar
1113     end else if ProcXMLBlock(nodo2, blq) then begin  //definición de bloque anidado
1114       //No es necesario procesar
1115     end else if UpCase(nodo2.NodeName) = '#COMMENT' then begin
1116       //solo para evitar que de mensaje de error
1117     end else begin
1118       raise ESynFacilSyn.Create(Format(ERR_INVAL_LAB_BLK,[nodo2.NodeName]));
1119     end;
1120   end;
1121 end;
ProcXMLSectionnull1122 function TSynFacilSyn.ProcXMLSection(nodo: TDOMNode; blqPad: TFaSynBlock): boolean;
1123 //Verifica si el nodo tiene la etiqueta <SECCION>. De ser así, devuelve TRUE y lo procesa.
1124 //Si encuentra error, genera una excepción.
1125 var
1126   i: integer;
1127   tStart, tFolding, tName, tParent : TFaXMLatrib;
1128   blq : TFaSynBlock;
1129   tBackCol, tUnique: TFaXMLatrib;
1130   nodo2  : TDOMNode;
1131   tStartPos: TFaXMLatrib;
1132   tFirstSec: TFaXMLatrib;
1133   tTokenStart: TFaXMLatrib;
1134   Success: boolean;
1135 begin
1136   if UpCase(nodo.NodeName) <> 'SECTION' then exit(false);
1137   Result := true;  //encontró
1138   //lee atributos
1139   tStart    := ReadXMLParam(nodo,'Start');
1140   tTokenStart:= ReadXMLParam(nodo,'TokenStart');
1141   tName     := ReadXMLParam(nodo,'Name');
1142   tFolding  := ReadXMLParam(nodo,'Folding');    //Falso, si no existe
1143   tParent   := ReadXMLParam(nodo,'Parent');
1144   tBackCol  := ReadXMLParam(nodo,'BackCol');
1145   tUnique   := ReadXMLParam(nodo,'Unique');
1146   tFirstSec := ReadXMLParam(nodo,'FirstSec');
1147   //validaciones
1148   if not tFolding.hay then tFolding.bol:=true;  //por defecto
1149   if not tName.hay then tName.val:='Sec'+IntToStr(lisBlocks.Count+1);
1150   CheckXMLParams(nodo, 'Start TokenStart Name Folding Parent BackCol Unique FirstSec');
1151   if tParent.hay then begin //se especificó blqPad padre
1152     blqPad := SearchBlock(tParent.val, Success);  //ubica blqPad
1153     if not Success then raise ESynFacilSyn.Create(ERR_BLK_NO_DEFINED + tParent.val);
1154   end;
1155   //crea la sección, con el bloque padre indicado, o el que viene en el parámetro
1156   blq := CreateBlock(tName.val, tFolding.bol, blqPad);
1157   blq.IsSection:=true;
1158   if tStart.hay then begin   //configuración normal con "Start"
1159     if tFirstSec.hay then begin  //hay primera sección
1160       AddFirstSectToTok(tStart.val, 0, blq)
1161     end else begin               //sección normal
1162       AddIniSectToTok(tStart.val, 0, blq);
1163     end;
1164   end else if tTokenStart.hay  then begin  //configuración indicando nombre de token
1165     {Se usará la misma función AddIniSectToTok(), para encontrar al token, pero
1166      formalmente debería usarse una función especial para ubicar al token usnado
1167      su nombre}
1168     AddIniSectToTok(tTokenStart.val, 0, blq);
1169   end;
1170   if tBackCol.hay then begin
1171     if UpCase(tBackCol.val)='TRANSPARENT' then blq.BackCol:= COL_TRANSPAR
1172     else blq.BackCol:= tBackCol.col;   //lee color
1173   end;
1174   if tUnique.hay then blq.UniqSec:=tUnique.bol;  //lee Unique
1175   ////////// explora nodos hijos //////////
1176   for i := 0 to nodo.ChildNodes.Count-1 do begin
1177       nodo2 := nodo.ChildNodes[i];
1178       if UpCAse(nodo2.NodeName)='START' then begin  //definición alternativa de delimitador
1179         tStartPos := ReadXMLParam(nodo2,'StartPos');
1180         CheckXMLParams(nodo2, 'StartPos');
1181         //agrega la referecnia del bloque al nuevo token delimitador
1182         AddIniSectToTok(trim(AnsiString(nodo2.TextContent)), tStartPos.n, blq);
1183       end else if ProcXMLSection(nodo2, blq) then begin  //definición de sección
1184         //No es necesario procesar
1185       end else if ProcXMLBlock(nodo2, blq) then begin  //definición de bloque anidado
1186         //No es necesario procesar
1187       end else if UpCase(nodo2.NodeName) = '#COMMENT' then begin
1188         //solo para evitar que de mensaje de error
1189       end else begin
1190         raise ESynFacilSyn.Create(Format(ERR_INVAL_LAB_SEC,[nodo2.NodeName]));
1191       end;
1192   end;
1193 end;
1194 procedure TSynFacilSyn.LoadFromFile(const Filename: string);
1195 var
1196   fs: TFileStream;
1197 begin
1198   try
1199     fs := TFileStream.Create(Filename, fmOpenRead);
1200     fs.Position := 0;
1201 
1202     LoadFromStream(fs);
1203     fs.Free;
1204   except
1205     on e: Exception do begin
1206       //completa el mensaje
1207       e.Message:=ERROR_LOADING_ + Filename + #13#10 + e.Message;
1208       fs.Free;
1209       raise   //genera de nuevo
1210     end;
1211   end;
1212 end;
1213 procedure TSynFacilSyn.Rebuild;
1214 {Configura los tokens delimitados de acuerdo a la sintaxis definida actualmente, de
1215  forma que se optimice el procesamiento.
1216  Todos los tokens que se procesen aquí, deben tener delimitador inicial símbolo}
1217 var
1218   i,j     : integer;
1219   r       : TTokSpec;
1220   dSexc   : boolean;  //indica que el delimitador inicial es exclusivo.
1221 
delStart1Exclusnull1222   function delStart1Exclus(cad0: string): boolean;
1223   {Indica si el token símbolo es de 1 caracter de ancho y no otro token símbolo que empiece
1224    con ese caracter}
1225   var i: integer;
1226       cad: string;
1227   begin
1228     Result := true;  //se asume que si es exclusivo
1229     if length(cad0)<>1 then exit(false);
1230     for i := 0 to High(mSym0) do begin
1231       cad := mSym0[i].txt;
1232       if cad  <> cad0 then begin  //no considera al mismo
1233         if cad0[1] = cad[1] then exit(false);  //no es
1234       end;
1235     end;
1236   end;
1237 
1238 begin
1239   {$IFDEF DebugMode}
1240   DebugLn('---------símbolos leidos: mSym0[]----------');
1241   for i:=0 to High(mSym0) do
1242     DebugLn('  bloque: '+ mSym0[i].txt + ',' + StringReplace(mSym0[i].dEnd, #13,#25,[]));
1243   DebugLn('---------simplificando símbolos----------');
1244   {$ENDIF}
1245   //Explora los símbolos para optimizar el procesamiento
1246   setlength(mSym,0);  //limpia, porque vamos a reconstruir
1247   for i := 0 to High(mSym0) do begin
1248     r := mSym0[i];
1249     dSexc:=delStart1Exclus(r.txt);  //ve si es de 1 caracter y exclusivo
1250     if          dSexc and (r.typDel=tdConten1) then begin
1251       //Token por contenido, que se puede optimizar
1252       {$IFDEF DebugMode}
1253       DebugLn('  [' + r.txt[1] + '] -> @metTokCont1 (Token Por Conten. inicio exclusivo)');
1254       {$ENDIF}
1255       fProcTable[r.txt[1]] := @metTokCont1;
1256     end else if dSexc and (r.typDel=tdConten2) then begin
1257       //Token por contenido, que se puede optimizar
1258       {$IFDEF DebugMode}
1259       DebugLn('  [' + r.txt[1] + '] -> @metTokCont2 (Token Por Conten. inicio exclusivo)');
1260       {$ENDIF}
1261       fProcTable[r.txt[1]] := @metTokCont2;
1262     end else if dSexc and (r.typDel=tdConten3) then begin
1263       //Token por contenido, que se puede optimizar
1264       {$IFDEF DebugMode}
1265       DebugLn('  [' + r.txt[1] + '] -> @metTokCont3 (Token Por Conten. inicio exclusivo)');
1266       {$ENDIF}
1267       fProcTable[r.txt[1]] := @metTokCont3;
1268     end else if dSexc and (r.typDel=tdConten4) then begin
1269       //Token por contenido, que se puede optimizar
1270       {$IFDEF DebugMode}
1271       DebugLn('  [' + r.txt[1] + '] -> @metTokCont4 (Token Por Conten. inicio exclusivo)');
1272       {$ENDIF}
1273       fProcTable[r.txt[1]] := @metTokCont4;
1274     end else if dSexc and (r.typDel=tdUniLin) and (r.txt=r.dEnd) and (r.chrEsc=#0) then begin
1275       //Caso típico de cadenas. Es procesable por nuestra función "metUniLin1"
1276       {$IFDEF DebugMode}
1277       DebugLn('  [' + r.txt[1] + '] -> @metUniLin1 (uniLin c/delims iguales de 1 car)');
1278       {$ENDIF}
1279       fProcTable[r.txt[1]] := @metUniLin1;
1280       fAtriTable[r.txt[1]] := r.tTok; //para que metUniLin1() lo pueda recuperar
1281     //busca tokens una línea con delimitador de un caracter
1282     end else if dSexc and (r.typDel=tdUniLin) and (r.dEnd=#13) then begin
1283       //Caso típico de comentarios. Es procesable por nuestra función "metFinLinea"
1284       {$IFDEF DebugMode}
1285       DebugLn('  [' + r.txt[1] + '] -> @metFinLinea (uniLin con dStart de 1 car y dEnd = #13)');
1286       {$ENDIF}
1287       fProcTable[r.txt[1]] := @metFinLinea;
1288       fAtriTable[r.txt[1]] := r.tTok; //para que metFinLinea() lo pueda recuperar
1289       { TODO : Se podría crear un procedimiento para manejar bloques multilíneas
1290        con delimitador inicial exclusivo y así optimizar su procesamiento porque puede
1291        tornarse pesado en la forma actual. }
1292     end else if dSexc and (r.typDel=tdNull) and not r.openBlk and not r.closeBlk and
1293                 not r.OpenSec then begin
1294       //Es símbolo especial de un caracter, exclusivo, que no es parte de token delimitado
1295       //ni es inicio o fin de bloque
1296       {$IFDEF DebugMode}
1297       DebugLn('  [' + r.txt[1] + '] -> @metSym1Car (símbolo simple de 1 car)');
1298       {$ENDIF}
1299       fProcTable[r.txt[1]] := @metSym1Car;
1300       fAtriTable[r.txt[1]] := r.tTok; //para que metSym1Car() lo pueda recuperar
1301     end else begin //no se puede simplificar.
1302       //Lo agrega a la tabla de símbolos para búsqueda normal.
1303       CreaBuscTokEspec(mSym, r.txt, j);  //No puede usar CreaBuscSymEspec(), porque usa mSymb0
1304       mSym[j] := r;  //actualiza o agrega
1305     end
1306   end;
1307   //termina el proceso
1308   RebuildSymbols;
1309   if CurrentLines <> nil then begin //Hay editor asignado
1310     ScanAllRanges;  {Necesario, porque se ha reconstruido los TTokSpec y
1311                        los valores de "fRange" de las líneas, están "perdidos"}
1312   end;
1313   {$IFDEF DebugMode}
1314   DebugLn('--------------------------------');
1315   {$ENDIF}
1316 end;
1317 /////////// manejo de bloques
1318 procedure TSynFacilSyn.StartBlock(ABlockType: Pointer; IncreaseLevel: Boolean); inline;
1319 //Procedimiento general para abrir un bloque en el resaltador
1320 begin
1321 //  if IsCollectingNodeInfo then // llutti
1322 //  begin // llutti
1323 //    CollectNodeInfo(False, ABlockType, IncreaseLevel); // llutti
1324 //  end; // llutti
1325   StartCodeFoldBlock(ABlockType, IncreaseLevel);
1326 //  CodeFoldRange.Add(ABlockType, IncreaseLevel);
1327 end;
1328 procedure TSynFacilSyn.EndBlock(DecreaseLevel: Boolean); inline;
1329 //Procedimiento general para cerrar un bloque en el resaltador
1330 begin
1331 //  BlockType := TopCodeFoldBlockType; // llutti
1332 //  if IsCollectingNodeInfo then // llutti
1333 //  begin // llutti
1334 //    CollectNodeInfo(True, BlockType, DecreaseLevel); // llutti
1335 //  end; // llutti
1336   EndCodeFoldBlock(DecreaseLevel);
1337 //  CodeFoldRange.Pop(DecreaseLevel);
1338 end;
1339 procedure TSynFacilSyn.StartBlockAndFirstSec(const blk, firstSec: TfaSynBlock);
1340 {Abre un bloque TfaSynBlock, verifica si tiene una primera sección para abrir.}
1341 var Cancel: boolean;
1342 begin
1343   Cancel := false;
1344   if blk.OnBeforeOpen<>nil then blk.OnBeforeOpen(blk, Cancel);
1345   if Cancel then exit;
1346   StartBlock(blk, blk.showFold);
1347   //verifica si hay primera sección para abrir
1348   if firstSec <> nil then
1349     StartBlock(firstSec, firstSec.showFold);
1350 end;
1351 procedure TSynFacilSyn.StartBlockFa(const blk: TfaSynBlock);
1352 {Abre un bloque TfaSynBlock.}
1353 var Cancel: boolean;
1354 begin
1355   Cancel := false;
1356   if blk.OnBeforeOpen<>nil then blk.OnBeforeOpen(blk, Cancel);
1357   if Cancel then exit;
1358   StartBlock(blk, blk.showFold);
1359 end;
1360 procedure TSynFacilSyn.EndBlockFa(const blk: TfaSynBlock);
1361 {Cierra un bloque TfaSynBlock. El parámetro blk, no debería ser necesario, puesto que
1362 se supone que siemprer se cerrará el último abierto.}
1363 var Cancel: boolean;
1364 begin
1365   Cancel := false;
1366   if blk.OnBeforeClose<>nil then blk.OnBeforeClose(blk, Cancel);
1367   if Cancel then exit;
1368   EndBlock(blk.showFold);
1369 end;
TopBlocknull1370 function TSynFacilSyn.TopBlock: TFaSynBlock;
1371 //Función genérica para devolver el último bloque abierto. Si no hay ningún bloque
1372 //abierto, devuelve "MainBlk".
1373 //Es una forma personalizada de TopCodeFoldBlockType()
1374 var
1375   Fold: TSynCustomCodeFoldBlock;
1376 begin
1377   Fold := CodeFoldRange.Top;  //CodeFoldRange nunca denería ser NIL
1378   if Fold = nil then
1379     Result := MainBlk  //está en el primer nivel
1380   else begin
1381     Result := TFaSynBlock(Fold.BlockType);
1382     if Result = nil then
1383       Result := MainBlk;  //protección
1384   end;
1385 end;
TSynFacilSyn.TopBlockOpacnull1386 function TSynFacilSyn.TopBlockOpac: TFaSynBlock;
1387 //Función genérica para devolver el último bloque abierto con color de fondo.
1388 var
1389   Fold: TSynCustomCodeFoldBlock;
1390 begin
1391   //profundiza hasta encontrar un bloque con color opaco
1392    Fold := CodeFoldRange.Top;
1393    while (Fold <> nil) and (Fold.BlockType<>nil) and
1394          (TFaSynBlock(Fold.BlockType).BackCol=COL_TRANSPAR) do begin
1395      Fold := Fold.Parent;
1396    end;
1397    //si no encontró devuelve el bloque principal
1398    if (Fold = nil) or (Fold.BlockType=nil) then begin
1399      Result := MainBlk
1400    end else begin
1401      Result := TFaSynBlock(Fold.BlockType);
1402    end;
1403 end;
TSynFacilSyn.SearchBlocknull1404 function TSynFacilSyn.SearchBlock(blk: string; out Success: boolean): TFaSynBlock;
1405 {Busca un bloque por su nombre. Se ignora la caja.
1406  Si la búsqueda tuvo éxito, pone la bandera "Success" en TRUE}
1407 var i: integer;
1408 begin
1409   Result := nil;  //valor por defecto. Es un valor "válido".
1410   Success := false;
1411   if UpCase(blk) = 'NONE' then begin
1412     Success := true;
1413     exit;
1414   end;
1415   if UpCase(blk) = 'MAIN' then begin
1416     Result := MainBlk;
1417     Success := true;
1418     exit;
1419   end;
1420   for i := 0 to lisBlocks.Count-1 do
1421     if Upcase(lisBlocks[i].name) = Upcase(blk) then begin
1422        Result := lisBlocks[i];  //devuelve referencia
1423        Success := true;
1424        exit;
1425     end;
1426   //no se encontró el blqPad pedido
1427 end;
1428 
1429 procedure TSynFacilSyn.ProcTokenDelim(const d: TTokSpec);
1430 {Procesa un posible token delimitador. Debe llamarse después de que se ha reconocido
1431  el token especial y el puntero apunte al siguiente token.}
1432   procedure CheckForOpenBlk(const d: TTokSpec); //inline;
1433   {Abre el bloque actual, verificando si está en el bloque valído}
1434   var
1435       CurBlk: TFaSynBlock;
1436       CurBlk_Parent: TFaSynBlock;
1437       blkToOpen: TFaSynBlock;
1438   begin
1439     CurBlk := TopBlock();  //lee bloque superior (el actual)
1440     if CurBlk.IsSection then begin
1441       //Estamos en un bloque de sección
1442       CurBlk_Parent := TopCodeFoldBlock(1);  {Lee bloque superior, en donde se ha abierto, que no
1443                                               siempe coincidirá con CurBlk.parentBlk }
1444       for blkToOpen in d.BlksToOpen do begin
1445         //verifica si se cumplen condiciones para abrir el bloque
1446         if blkToOpen.parentBlk = nil then begin //se abre en cualquier parte
1447           {Un bloque al mismo nivel de una sección, la cierra siempre}
1448           EndBlockFa(CurBlk);  //cierra primero la sección anterior
1449           StartBlockAndFirstSec(blkToOpen, d.firstSec);
1450           break;   //sale
1451         end else if blkToOpen.parentBlk = CurBlk  then begin
1452           //Corresponde abrir el bloque dentro de esta sección
1453           {No se verifica si hay sección anterior porque estamos en una sección y se sabe
1454           que un sección no puede contener otras secciones.}
1455           StartBlockAndFirstSec(blkToOpen, d.firstSec);
1456           break;     //sale
1457         end else if blkToOpen.parentBlk = CurBlk_Parent then begin
1458           {No correspondía abrir en esa sección, pero se aplica al bloque padre,
1459           entonces está al mismo nivel que la sección actual y debería cerrarla primero}
1460           EndBlockFa(CurBlk);  //cierra primero la sección anterior
1461           StartBlockAndFirstSec(blkToOpen, d.firstSec);
1462           break;     //sale
1463         end;
1464       end;
1465     end else begin
1466       //Estamos dentro de un bloque común
1467       for blkToOpen in d.BlksToOpen do begin
1468         //verifica si se cumplen condiciones para abrir el bloque
1469         if blkToOpen.parentBlk = nil then begin //se abre en cualquier parte
1470           StartBlockAndFirstSec(blkToOpen, d.firstSec);
1471           break;   //sale
1472         end else if blkToOpen.parentBlk = CurBlk then begin
1473           //Corresponde abrir en este bloque
1474           StartBlockAndFirstSec(blkToOpen, d.firstSec);
1475           break;     //sale
1476         end;
1477       end;
1478     end;
1479   end;
CheckForOpenSecnull1480   function CheckForOpenSec(const d: TTokSpec): boolean; //inline;
1481   {Abre una sección si se cunplen las condiciones. De ser el caso, cierra primero una
1482    posible sección previa. Si abre la sección devuelve TRUE. }
1483   var
1484       curBlk: TFaSynBlock;
1485       CurBlk_Parent: TFaSynBlock;
1486       SecToOpen: TFaSynBlock;
1487   begin
1488     CurBlk := TopBlock();  //lee bloque superior (el actual)
1489     if curBlk.IsSection then begin //verifica si está en un bloque de sección
1490       //Estamos en un bloque de sección. ¿Será de alguna de las secciones que maneja?
1491       CurBlk_Parent := TopCodeFoldBlock(1);  {Lee bloque superior, en donde se ha abierto, que no
1492                                               siempe coincidirá con CurBlk.parentBlk }
1493       for SecToOpen in d.SecsToOpen do begin
1494         //verifica si se cumplen condiciones para abrir el bloque
1495         if SecToOpen.parentBlk = nil then begin //se abre en cualquier parte
1496           {Una sección al mismo nivel de una sección, la cierra siempre}
1497           if (SecToOpen=curBlk) and curBlk.UniqSec then exit(false); //verificación
1498           EndBlockFa(CurBlk);  //cierra primero la sección anterior
1499           StartBlockFa(SecToOpen);  //abre una nueva sección
1500           exit(true);  //sale con TRUE
1501         end else if SecToOpen.parentBlk = CurBlk_Parent then begin
1502           //Está en el bloque para el que se ha definido
1503           //Debe cerrar primero la sección anterior, porque está al mismo nivel
1504           if (SecToOpen=curBlk) and curBlk.UniqSec then exit(false); //verificación
1505           EndBlockFa(curBlk);  //cierra primero la sección anterior
1506           StartBlockFa(SecToOpen);  //abre una nueva sección
1507           exit(true);  //sale con TRUE
1508         end else if SecToOpen.parentBlk = CurBlk_Parent.parentBlk then begin
1509           //Está en el bloque para el que se ha definido, pero hay abierta otra sección
1510           //Debe cerrar primero la sección anterior, y la anterior.
1511 //          if (SecToOpen=curBlk) and curBlk.UniqSec then exit(false); //verificación
1512           EndBlockFa(curBlk);  //cierra primero la sub-sección anterior
1513           EndBlockFa(curBlk);  //cierra primero la sección anterior
1514           StartBlockFa(SecToOpen);  //abre una nueva sección
1515           exit(true);  //sale con TRUE
1516         end else if SecToOpen.parentBlk = CurBlk then begin
1517           //Está en el bloque que se ha definido como padre
1518           StartBlockFa(SecToOpen);  //abre una nueva sección dentro de la sección
1519         end;
1520       end;
1521       Result := false;  //no abrió
1522     end else begin
1523       //No está en un bloque de sección, entonces debe estar en un bloque (aunque sea MainBlk)
1524       //verifica si corresponde abrir esta sección
1525       for SecToOpen in d.SecsToOpen do begin
1526         //verifica si se cumplen condiciones para abrir el bloque
1527         if SecToOpen.parentBlk = nil then begin //se abre en cualquier parte
1528           StartBlockFa(SecToOpen);
1529           exit(true);  //sale con TRUE
1530         end else if SecToOpen.parentBlk = curBlk then begin
1531           //Corresponde abrir en este bloque
1532           StartBlockFa(SecToOpen);
1533           exit(true);  //sale con TRUE
1534         end;
1535       end;
1536       Result := false;  //no abrió
1537     end;
1538   end;
1539   procedure CheckForCloseBlk(const BlksToClose: array of TFaSynBlock); //inline;
1540   {Verifica si el bloque más reciente del plegado, está en la lista de bloques
1541    que cierra "d.BlksToClose". De ser así cierra el bloque}
1542   var
1543     CurBlk: TFaSynBlock;
1544     CurBlk_Parent: TFaSynBlock;
1545     BlkToClose: TFaSynBlock;
1546   begin
1547     CurBlk := TopBlock();  //lee bloque superior
1548     //verifica si estamos en medio de una sección
1549     if CurBlk.IsSection then begin //verifica si es bloque de sección
1550       {Es sección, el bloque actual debe ser el bloque padre, porque por definición
1551       los tokens no cierran secciones (a menos que abran bloques).}
1552       CurBlk_Parent := TopCodeFoldBlock(1);  //lee bloque superior
1553       for BlkToClose in BlksToClose do begin
1554         if BlkToClose = CurBlk_Parent  then begin  //coincide
1555           //Antes de cerrar el blqoque padre, debe cerrar la sección actual
1556           EndBlockFa(CurBlk);  //cierra primero la sección
1557 //            EndBlockFa(CurBlk_Parent.showFold)  //cierra bloque
1558           CloseThisBlk := BlkToClose; //marca para cerrar en el siguuiente token
1559           break;
1560         end;
1561       end;
1562     end else begin
1563       //Estamos dentro de un bloque común
1564       for BlkToClose in BlksToClose do begin
1565         if BlkToClose = CurBlk  then begin  //coincide
1566 //            EndBlockFa(CurBlk_Parent.showFold)  //cierra bloque
1567           CloseThisBlk := BlkToClose; //marca para cerrar en el siguuiente token
1568           break;
1569         end;
1570       end;
1571     end;
1572   end;
1573 
1574 var
1575   abrioSec: Boolean;
1576 begin
1577   case d.typDel of
1578   tdNull: begin       //token que no es delimitador de token
1579       fTokenID := d.tTok; //no es delimitador de ningún tipo, pone su atributo
1580       //un delimitador común puede tener plegado de bloque
1581       if d.closeBlk then begin //verifica primero, si es cierre de algún bloque
1582         CheckForCloseBlk(d.BlksToClose);  //cierra primero
1583       end;
1584       abrioSec := false;
1585       if d.OpenSec then //Verifica primero si es bloque de sección
1586         abrioSec := CheckForOpenSec(d);
1587       if not abrioSec then  //prueba si abre como bloque
1588         if d.openBlk then CheckForOpenBlk(d); //verifica como bloque normal
1589     end;
1590   tdUniLin: begin  //delimitador de token de una línea.
1591       //Se resuelve siempre en la misma línea.
1592       fTokenID := d.tTok;   //asigna token
1593       delTok := d.dEnd;   //para que esté disponible al explorar la línea actual.
1594       folTok := false;    //No tiene sentido el plegado, en token de una línea.
1595       chrEsc := d.chrEsc; //caracter de escape
1596       if posFin=tamLin then exit;  //si está al final, necesita salir con fTokenID fijado.
1597       d.pRange;  //ejecuta función de procesamiento
1598       //Se considera que este tipo de tokens, puede ser también inicio o fin de bloque
1599       if d.closeBlk then begin //verifica primero, si es cierre de algún bloque
1600         CheckForCloseBlk(d.BlksToClose);  //cierra primero
1601       end;
1602       abrioSec := false;
1603       if d.OpenSec then //Verifica primero si es bloque de sección
1604         abrioSec := CheckForOpenSec(d);
1605       if not abrioSec then  //prueba si abre como bloque
1606         if d.openBlk then CheckForOpenBlk(d); //verifica como bloque normal
1607     end;
1608   tdMulLin: begin  //delimitador de token multilínea
1609       //Se pueden resolver en la línea actual o en las siguientes líneas.
1610       fTokenID := d.tTok;   //asigna token
1611       delTok := d.dEnd;    //para que esté disponible al explorar las sgtes. líneas.
1612       folTok := d.folTok;  //para que esté disponible al explorar las sgtes. líneas.
1613       chrEsc := d.chrEsc;  //caracter de escape
1614       if folTok then StartBlockFa(MulTokBlk);  //abre al inicio del token
1615       fRange := @d;    //asigna rango apuntando a este registro
1616       if posFin=tamLin then exit;  //si está al final, necesita salir con fTokenID fijado.
1617       d.pRange;  //ejecuta función de procesamiento
1618     end;
1619   tdConten1: begin  //delimitador de token por contenido 1
1620       dec(posFin);    //ajusta para que se procese correctamente
1621       metTokCont1;    //este método se encarga
1622     end;
1623   tdConten2: begin  //delimitador de token por contenido 1
1624       dec(posFin);    //ajusta para que se procese correctamente
1625       metTokCont2;    //este método se encarga
1626     end;
1627   tdConten3: begin  //delimitador de token por contenido 1
1628       dec(posFin);    //ajusta para que se procese correctamente
1629       metTokCont3;    //este método se encarga
1630     end;
1631   tdConten4: begin  //delimitador de token por contenido 1
1632       dec(posFin);    //ajusta para que se procese correctamente
1633       metTokCont4;    //este método se encarga
1634     end;
1635   else
1636     fTokenID := d.tTok; //no es delimitador, solo toma su atributo.
1637   end;
1638 end;
1639 //Rutinas de procesamiento de Identificadores especiales
1640 procedure TSynFacilSyn.metIdentEsp(var mat: TArrayTokSpec); //inline;
1641 //Procesa el identificador actual con la matriz indicada
1642 var i: integer;
1643 begin
1644   repeat inc(posFin)
1645   until not CharsIdentif[fLine[posFin]];
1646   fStringLen := posFin - posIni - 1;  //calcula tamaño - 1
1647   fToIdent := Fline + posIni + 1;  //puntero al identificador + 1
1648   fTokenID := tnIdentif;  //identificador común
1649   for i := 0 to High(mat) do begin
1650     if KeyComp(mat[i])  then begin
1651       ProcTokenDelim(mat[i]); //verifica si es delimitador
1652       exit;
1653     end;
1654   end;
1655 end;
1656 procedure TSynFacilSyn.metA;begin metIdentEsp(mA);end;
1657 procedure TSynFacilSyn.metB;begin metIdentEsp(mB);end;
1658 procedure TSynFacilSyn.metC;begin metIdentEsp(mC);end;
1659 procedure TSynFacilSyn.metD;begin metIdentEsp(mD);end;
1660 procedure TSynFacilSyn.metE;begin metIdentEsp(mE);end;
1661 procedure TSynFacilSyn.metF;begin metIdentEsp(mF);end;
1662 procedure TSynFacilSyn.metG;begin metIdentEsp(mG);end;
1663 procedure TSynFacilSyn.metH;begin metIdentEsp(mH);end;
1664 procedure TSynFacilSyn.metI;begin metIdentEsp(mI);end;
1665 procedure TSynFacilSyn.metJ;begin metIdentEsp(mJ);end;
1666 procedure TSynFacilSyn.metK;begin metIdentEsp(mK);end;
1667 procedure TSynFacilSyn.metL;begin metIdentEsp(mL);end;
1668 procedure TSynFacilSyn.metM;begin metIdentEsp(mM);end;
1669 procedure TSynFacilSyn.metN;begin metIdentEsp(mN);end;
1670 procedure TSynFacilSyn.metO;begin metIdentEsp(mO);end;
1671 procedure TSynFacilSyn.metP;begin metIdentEsp(mP);end;
1672 procedure TSynFacilSyn.metQ;begin metIdentEsp(mQ);end;
1673 procedure TSynFacilSyn.metR;begin metIdentEsp(mR);end;
1674 procedure TSynFacilSyn.metS;begin metIdentEsp(mS);end;
1675 procedure TSynFacilSyn.metT;begin metIdentEsp(mT);end;
1676 procedure TSynFacilSyn.metU;begin metIdentEsp(mU);end;
1677 procedure TSynFacilSyn.metV;begin metIdentEsp(mV);end;
1678 procedure TSynFacilSyn.metW;begin metIdentEsp(mW);end;
1679 procedure TSynFacilSyn.metX;begin metIdentEsp(mX);end;
1680 procedure TSynFacilSyn.metY;begin metIdentEsp(mY);end;
1681 procedure TSynFacilSyn.metZ;begin metIdentEsp(mZ);end;
1682 
1683 procedure TSynFacilSyn.metA_; begin metIdentEsp(mA_);end;
1684 procedure TSynFacilSyn.metB_; begin metIdentEsp(mB_);end;
1685 procedure TSynFacilSyn.metC_; begin metIdentEsp(mC_);end;
1686 procedure TSynFacilSyn.metD_; begin metIdentEsp(mD_);end;
1687 procedure TSynFacilSyn.metE_; begin metIdentEsp(mE_);end;
1688 procedure TSynFacilSyn.metF_; begin metIdentEsp(mF_);end;
1689 procedure TSynFacilSyn.metG_; begin metIdentEsp(mG_);end;
1690 procedure TSynFacilSyn.metH_; begin metIdentEsp(mH_);end;
1691 procedure TSynFacilSyn.metI_; begin metIdentEsp(mI_);end;
1692 procedure TSynFacilSyn.metJ_; begin metIdentEsp(mJ_);end;
1693 procedure TSynFacilSyn.metK_; begin metIdentEsp(mK_);end;
1694 procedure TSynFacilSyn.metL_; begin metIdentEsp(mL_);end;
1695 procedure TSynFacilSyn.metM_; begin metIdentEsp(mM_);end;
1696 procedure TSynFacilSyn.metN_; begin metIdentEsp(mN_);end;
1697 procedure TSynFacilSyn.metO_; begin metIdentEsp(mO_);end;
1698 procedure TSynFacilSyn.metP_; begin metIdentEsp(mP_);end;
1699 procedure TSynFacilSyn.metQ_; begin metIdentEsp(mQ_);end;
1700 procedure TSynFacilSyn.metR_; begin metIdentEsp(mR_);end;
1701 procedure TSynFacilSyn.metS_; begin metIdentEsp(mS_);end;
1702 procedure TSynFacilSyn.metT_; begin metIdentEsp(mT_);end;
1703 procedure TSynFacilSyn.metU_; begin metIdentEsp(mU_);end;
1704 procedure TSynFacilSyn.metV_; begin metIdentEsp(mV_);end;
1705 procedure TSynFacilSyn.metW_; begin metIdentEsp(mW_);end;
1706 procedure TSynFacilSyn.metX_; begin metIdentEsp(mX_);end;
1707 procedure TSynFacilSyn.metY_; begin metIdentEsp(mY_);end;
1708 procedure TSynFacilSyn.metZ_; begin metIdentEsp(mZ_);end;
1709 procedure TSynFacilSyn.metDol;begin metIdentEsp(mDol);end;
1710 procedure TSynFacilSyn.metArr;begin metIdentEsp(mArr);end;
1711 procedure TSynFacilSyn.metPer;begin metIdentEsp(mPer);end;
1712 procedure TSynFacilSyn.metAmp;begin metIdentEsp(mAmp);end;
1713 procedure TSynFacilSyn.metC3;begin metIdentEsp(mC3);end;
1714 procedure TSynFacilSyn.metUnd;begin metIdentEsp(m_);end;
1715 //Rutina única de procesamiento de Símbolos especiales
1716 procedure TSynFacilSyn.metSimbEsp;
1717 //Procesa un caracter que es inicio de símbolo y podría ser origen de un símbolo especial.
1718 var i: integer;
1719   nCarDisp: Integer;
1720 begin
1721   fTokenID := tnSymbol;  //identificador inicial por defecto
1722   //prepara para las comparaciones
1723   nCarDisp := tamLin-posIni;   //calcula caracteres disponibles hasta fin de línea
1724   fToIdent := Fline + posIni;  //puntero al identificador. Lo guarda para comparación
1725   //hay un nuevo posible delimitador. Se hace la búsqueda
1726   for i := 0 to High(mSym) do begin  //se empieza con los de mayor tamaño
1727     //fijamos nuevo tamaño para comparar
1728     fStringLen := length(mSym[i].txt);  //suponemos que tenemos esta cantidad de caracteres
1729     if fStringLen > nCarDisp then continue;  //no hay suficientes, probar con el siguiente
1730     if KeyComp(mSym[i]) then begin
1731       //¡Es símbolo especial!
1732       inc(posFin,fStringLen);  //apunta al siguiente token
1733       ProcTokenDelim(mSym[i]); //verifica si es delimitador
1734       exit;   //sale con el atributo asignado
1735     end;
1736   end;
1737   {No se encontró coincidencia.
1738    Ahora debemos continuar la exploración al siguiente caracter}
1739   posFin := posIni + 1;  //a siguiente caracter, y deja el actual como: fTokenID := tkSymbol
1740 end;
1741 //Funciones rápidas para la tabla de métodos (símbolos especiales)
1742 procedure TSynFacilSyn.metSym1Car;
1743 //Procesa tokens símbolo de un caracter de ancho.
1744 begin
1745   fTokenID := fAtriTable[charIni];   //lee atributo
1746   Inc(posFin);  //pasa a la siguiente posición
1747 end;
1748 //Funciones rápidas para la tabla de métodos (tokens delimitados)
1749 procedure TSynFacilSyn.metUniLin1;
1750 //Procesa tokens de una sola línea y con delimitadores iguales y de un solo caracter.
1751 begin
1752   fTokenID := fAtriTable[charIni];   //lee atributo
1753   Inc(posFin);  {no hay peligro en incrementar porque siempre se llama "metUniLin1" con
1754                  el caracter actual <> #0}
1755   while posFin <> tamLin do begin
1756     if fLine[posFin] = charIni then begin //busca fin de cadena
1757       Inc(posFin);
1758       if (fLine[posFin] <> charIni) then break;  //si no es doble caracter
1759     end;
1760     Inc(posFin);
1761   end;
1762 end;
1763 procedure TSynFacilSyn.metFinLinea;
1764 //Procesa tokens de una sola línea que va hasta el fin de línea.
1765 begin
1766   fTokenID := fAtriTable[charIni];   //lee atributo
1767   posFin := tamLin;  //salta rápidamente al final
1768 end;
1769 //Funciones llamadas por puntero y/o en medio de rangos. Estas funciones son llamadas
1770 //cuando se procesa un token especial que es inicio de token delimitado y no ha sido
1771 //optimizado para usar los métodos rápidos.
1772 procedure TSynFacilSyn.ProcEndLine;
1773 //Procesa hasta encontrar el fin de línea.
1774 begin
1775   posFin := tamLin;  //salta rápidamente al final
1776 end;
1777 procedure TSynFacilSyn.ProcRangeEndSym;
1778 {Procesa la línea actual buscando un delimitador símbolo (delTok).
1779  Si lo encuentra pone fRange a NIL. El tipo de token, debe estar ya asignado.}
1780 var p: PChar;
1781 begin
1782   //busca delimitador final
1783   p := strpos(fLine+posFin,PChar(delTok));
1784   if p = nil then begin   //no se encuentra
1785      posFin := tamLin;  //apunta al fin de línea
1786   end else begin  //encontró
1787      posFin := p + length(delTok) - fLine;
1788      fRange := nil;               //no necesario para tokens Unilínea
1789      if folTok then CloseThisBlk := MulTokBlk; //marca para cerrar en el siguuiente token
1790   end;
1791 end;
1792 procedure TSynFacilSyn.ProcRangeEndSym1;
1793 {Procesa la línea actual buscando un delimitador símbolo de un caracter.
1794  Si lo encuentra pone fRange a NIL. El tipo de token, debe estar ya asignado.}
1795 var p: PChar;
1796 begin
1797   //busca delimitador final
1798   if chrEsc=#0 then begin  //no hay caracter de escape
1799     p := strscan(fLine+posFin,delTok[1]);
1800   end else begin  //debe filtrar el caracter de escape
1801     p := strscan(fLine+posFin,delTok[1]);
1802     while (p<>nil) and ((p-1)^=chrEsc) do begin
1803       p := strscan(p+1,delTok[1]);
1804     end;
1805   end;
1806   if p = nil then begin   //no se encuentra
1807      posFin := tamLin;  //apunta al fin de línea
1808   end else begin  //encontró
1809      posFin := p + 1 - fLine;
1810      fRange := nil;              //no necesario para tokens Unilínea
1811      if folTok then CloseThisBlk := MulTokBlk; //marca para cerrar en el siguiente token
1812   end;
1813 end;
1814 procedure TSynFacilSyn.ProcRangeEndIden;
1815 {Procesa la línea actual buscando un delimitador identificador (delTok).
1816  Si lo encuentra pone fRange a rsUnknown. El tipo de token, debe estar ya asignado.}
1817 var p: Pchar;
1818     c1, c2: char;
1819 begin
1820   //busca delimitador final
1821   if CaseSensitive then
1822     p := strpos(fLine+posFin,PChar(delTok))
1823   else
1824     p := stripos(fLine+posFin,PChar(delTok));
1825   while p <> nil do begin   //definitivamente no se encuentra
1826     //verifica si es inicio de identificador
1827     c1:=(p-1)^;  {Retrocede. No debería haber problema en retroceder siempre, porque se
1828                   supone que se ha detectado el delimitador inicial, entonces siempre habrá
1829                   al menos un caracter}
1830     c2:=(p+length(delTok))^;   //apunta al final, puede ser el final de línea #0
1831     if (c1 in charsIniIden) or CharsIdentif[c1] or CharsIdentif[c2] then begin
1832       //está en medio de un identificador. No es válido.
1833       if CaseSensitive then
1834         p := strpos(p+length(delTok),PChar(delTok))  //busca siguiente
1835       else
1836         p := stripos(p+length(delTok),PChar(delTok));  //busca siguiente
1837     end else begin  //es el identificador buscado
1838       posFin := p + length(delTok) - fLine;  //puede terminar apuntándo a #0
1839       fRange := nil;               //no necesario para tokens Unilínea
1840       if folTok then CloseThisBlk := MulTokBlk; //marca para cerrar en el siguuiente token
1841       exit;
1842     end;
1843   end;
1844   //definitívamente no se encuentra
1845   posFin := tamLin;  //apunta al fin de línea
1846 end;
1847 ///////////////////////////////////////////////////////////////////////////////////
1848 procedure TSynFacilSyn.AddIniBlockToTok(dStart: string; TokPos: integer; blk: TFaSynBlock);
1849 //Agrega a un token especial, la referencia a un bloque, en la parte inicial.
1850 //Si hay error, genera excepción.
1851 var n: integer;
1852     tok : TPtrTokEspec;
1853 begin
1854   VerifDelim(dStart);  //puede generar excepción
1855   CreaBuscEspec(tok, dStart, TokPos); //busca o crea. Puede generar excepción
1856   //agrega referencia
1857   tok^.openBlk:=true;
1858   n:=High(tok^.BlksToOpen)+1;  //lee tamaño
1859   setlength(tok^.BlksToOpen,n+1);  //aumenta
1860   tok^.BlksToOpen[n]:=blk;  //escribe referencia
1861 end;
1862 procedure TSynFacilSyn.AddFinBlockToTok(dEnd: string; TokPos: integer; blk: TFaSynBlock);
1863 //Agrega a un token especial, la referencia a un bloque, en la parte final.
1864 //Si hay error, genera excepción.
1865 var n: integer;
1866     tok : TPtrTokEspec;
1867 begin
1868   VerifDelim(dEnd);  //puede generar excepción
1869   CreaBuscEspec(tok, dEnd, TokPos); //busca o crea. Puede generar excepción
1870   //agrega referencia
1871   tok^.closeBlk:=true;
1872   n:=High(tok^.BlksToClose)+1;  //lee tamaño
1873   setlength(tok^.BlksToClose,n+1);  //aumenta
1874   tok^.BlksToClose[n]:=blk;  //escribe referencia
1875 end;
1876 procedure TSynFacilSyn.AddIniSectToTok(dStart: string; TokPos: integer; blk: TFaSynBlock);
1877 //Agrega a un token especial, la referencia a una sección.
1878 //Si hay error, genera excepción.
1879 var n: integer;
1880     tok : TPtrTokEspec;
1881 begin
1882   VerifDelim(dStart);  //puede generar excepción
1883   CreaBuscEspec(tok, dStart, TokPos); //busca o crea. Puede generar excepción
1884   //agrega referencia
1885   tok^.OpenSec:=true;
1886   n:=High(tok^.SecsToOpen)+1;  //lee tamaño
1887   setlength(tok^.SecsToOpen,n+1);  //aumenta
1888   tok^.SecsToOpen[n]:=blk;  //escribe referencia
1889 end;
1890 procedure TSynFacilSyn.AddFirstSectToTok(dStart: string; TokPos: integer; blk: TFaSynBlock);
1891 //Agrega a un token especial, la referencia a una sección.
1892 var
1893   tok : TPtrTokEspec;
1894 begin
1895   VerifDelim(dStart);  //puede generar excepción
1896   CreaBuscEspec(tok, dStart, TokPos); //busca o crea. Puede generar excepción
1897   //agrega referencia
1898   tok^.firstSec := blk; //agrega referencia
1899 end;
CreateBlocknull1900 function TSynFacilSyn.CreateBlock(blkName: string; showFold: boolean = true;
1901                                   parentBlk: TFaSynBlock = nil): TFaSynBlock;
1902 //Crea un bloque en el resaltador y devuelve una referencia al bloque creado.
1903 var blk : TFaSynBlock;
1904 begin
1905   Result := nil;    //valor por defecto
1906   //if blkName = '' //No se verifica el nombre del bloque
1907   //Crea bloque
1908   blk:= TFaSynBlock.Create;
1909   blk.name     :=blkName;     //nombre de bloque
1910   blk.index    :=lisBlocks.Count; //calcula su posición
1911   blk.showFold := showFold;   //inidca si se muestra la marca de plegado
1912   blk.parentBlk:= parentBlk;  //asigna bloque padre
1913   blk.BackCol  := clNone;     //inicialmente sin color
1914   blk.IsSection:= false;
1915   blk.UniqSec  := false;
1916   blk.CloseParent :=false;
1917 
1918   lisBlocks.Add(blk);        //agrega a lista
1919   Result := blk;             //devuelve referencia
1920 end;
AddBlocknull1921 function TSynFacilSyn.AddBlock(dStart, dEnd: string; showFold: boolean = true;
1922                                parentBlk: TFaSynBlock = nil): TFaSynBlock;
1923 {Función pública para agregar un bloque a la sintaxis. Si encuentra error, genera una
1924 excepción}
1925 var blk : TFaSynBlock;
1926 begin
1927   Result := nil;    //valor por defecto
1928   //Crea bloque
1929   blk:= CreateBlock('',showFold,parentBlk);
1930   Result := blk;           //devuelve referencia
1931   //procesa delimitador inicial
1932   AddIniBlockToTok(dStart, 0, blk);  //agrega referencia
1933   //procesa delimitador final
1934   AddFinBlockToTok(dEnd, 0, blk);  //agrega referencia
1935 end;
AddSectionnull1936 function TSynFacilSyn.AddSection(dStart: string; showFold: boolean = true;
1937                                  parentBlk: TFaSynBlock = nil): TFaSynBlock;
1938 {Función pública para agregar una sección a un bloque a la sintaxis. Si encuentra
1939 genera una excepción}
1940 var blk : TFaSynBlock;
1941 begin
1942   Result := nil;    //valor por defecto
1943   //verificaciones
1944   if parentBlk = nil then begin
1945     parentBlk := MainBlk;  //NIL significa que es válido en el bloque principal
1946   end;
1947   //Crea bloque
1948   blk:= CreateBlock('',showFold,parentBlk);
1949   blk.IsSection:=true;
1950   Result := blk;           //devuelve referencia
1951   //procesa delimitador inicial
1952   AddIniSectToTok(dStart, 0, Blk);  //agrega referencia
1953 end;
TSynFacilSyn.AddFirstSectionnull1954 function TSynFacilSyn.AddFirstSection(dStart: string; showFold: boolean = true;
1955                                       parentBlk: TFaSynBlock = nil): TFaSynBlock;
1956 {Función pública para agregar una sección que se abre siempre al inicio de un bloque.
1957 Si encuentra error, genera una excepción}
1958 var
1959   blk : TFaSynBlock;
1960 begin
1961   Result := nil;    //valor por defecto
1962   //Una sección es también un bloque. Crea bloque
1963   blk:= CreateBlock('',showFold,parentBlk);
1964   blk.IsSection:=true;
1965   Result := blk;           //devuelve referencia
1966   //procesa delimitador inicial
1967   AddFirstSectToTok(dStart, 0, blk);
1968 end;
1969 //funciones para obtener información de bloques
TSynFacilSyn.NestedBlocksnull1970 function TSynFacilSyn.NestedBlocks: Integer;
1971 //Devuelve la cantidad de bloques anidados en la posición actual. No existe un contador
1972 //en el resaltador para este valor (solo para bloques con marca de pleagdo visible).
1973 var
1974   Fold: TSynCustomCodeFoldBlock;
1975 begin
1976   Result:=-1;  //para compensar el bloque que se crea al inicio
1977   if (CodeFoldRange<>nil) then begin
1978     Fold := CodeFoldRange.Top;
1979     while Fold <> nil do begin
1980 //if Fold.BlockType = nil then debugln('--NIL') else debugln('--'+TFaSynBlock(Fold.BlockType).name);
1981       inc(Result);
1982       Fold := Fold.Parent;
1983     end;
1984   end;
1985 end;
TSynFacilSyn.NestedBlocksBeginnull1986 function TSynFacilSyn.NestedBlocksBegin(LineNumber: integer): Integer;
1987 //Devuelve la cantidad de bloques anidados al inicio de la línea.
1988 begin
1989   if LineNumber = 0 then  //primera línea
1990     Result := 0
1991   else begin
1992     SetRange(CurrentRanges[LineNumber - 1]);
1993     Result := NestedBlocks;
1994   end;
1995 end;
TopCodeFoldBlocknull1996 function TSynFacilSyn.TopCodeFoldBlock(DownIndex: Integer): TFaSynBlock;
1997 //Función pública para TopCodeFoldBlockType() pero no devuelve puntero.
1998 begin
1999   Result := TFaSynBlock(TopCodeFoldBlockType(DownIndex));
2000   if Result = nil then   //esto solo podría pasar en el bloque principal
2001     Result := MainBlk;
2002 end;
SetHighlighterAtXYnull2003 function TSynFacilSyn.SetHighlighterAtXY(XY: TPoint): boolean;
2004 //Pone al resaltador en una posición específica del texto, como si estuviera
2005 //haciendo la exploración normal. Así se puede leer el estado.
2006 //La posición XY, empieza en (1,1). Si tuvo exito devuelve TRUE.
2007 var
2008   PosX, PosY: integer;
2009 //  Line: string;
2010   Start: Integer;
2011 begin
2012   Result := false;  //valor por defecto
2013   //validaciónes
2014   PosY := XY.Y -1;
2015   if (PosY < 0) or (PosY >= CurrentLines.Count) then exit;
2016   PosX := XY.X;
2017   if (PosX <= 0) then exit;
2018 {  Line := CurrentLines[PosY];
2019   //validación
2020   if PosX >= Length(Line)+1 then begin
2021     //Está al final o más. Simula el estado al final de la línea
2022     //Este bloque se puede quitar
2023     SetLine(Line);
2024     SetRange(CurrentRanges[PosY]);   //carga estado de rango al final
2025     fTokenId := tkEol;        //marca final
2026     posFin := length(Line)+1;
2027     posIni := posFin;
2028     //posTok := ??? no se puede regenerar sin explorar
2029     Result := TRUE;
2030     exit;
2031   end;}
2032   //explora línea
2033   StartAtLineIndex(PosY);   //posiciona y hace el primer Next()
2034   while not GetEol do begin
2035     Start := GetTokenPos + 1;
2036     if (PosX >= Start) and (PosX < posFin+1) then begin
2037       //encontró
2038       //Token := GetToken;  //aquí se puede leer el token
2039       Result := TRUE;
2040       exit;
2041     end;
2042     Next;
2043   end;
2044   //No lo ubicó. Está más allá del fin de línea
2045   Result := TRUE;
2046 end;
ExploreLinenull2047 function TSynFacilSyn.ExploreLine(XY: TPoint; out toks: TATokInfo;
2048                                   out CurTok: integer): boolean;
2049 //Explora la línea en la posición indicada. Devuelve la lista de tokens en toks[].
2050 //También indica el orden del token actual.
2051 //La posición XY, empieza en (1,1). Si tuvo exito devuelve TRUE.
2052 var
2053   PosX, PosY: integer;
2054   idx: Integer;
2055 begin
2056   Result := false;  //valor por defecto
2057   CurTok :=-1;       //valor por defecto
2058   idx := 0;
2059   setlength(toks,12);  //tamaño inicial
2060   //validaciónes
2061   PosY := XY.Y -1;
2062   if (PosY < 0) or (PosY >= CurrentLines.Count) then exit;
2063   PosX := XY.X;
2064   if (PosX <= 0) then exit;
2065   //explora línea
2066   StartAtLineIndex(PosY);   //posiciona y hace el primer Next()
2067   while not GetEol do begin
2068     //hay token
2069     if idx>high(toks) then
2070       setlength(toks, idx+12);  //aumenta espacio
2071     toks[idx].TokPos:=idx;
2072     toks[idx].txt := GetToken;
2073     toks[idx].length:=posFin - posIni;  //tamaño del token
2074     toks[idx].TokTyp:=fTokenID;
2075     toks[idx].posIni:=PosIni;
2076     toks[idx].IsIDentif:= (fLine[posIni] in charsIniIden);  //puede ser Keyword, o cualquier otro identificador
2077     toks[idx].curBlk := TopCodeFoldBlock(0);  //lee el rango
2078     Inc(idx);  //actualiza tamaño
2079 
2080     if (PosX > PosIni) and (PosX < posFin+1) then begin
2081       //encontró
2082       CurTok := idx-1;  //devuelve índice del token
2083       Result := TRUE;
2084     end;
2085     Next;
2086   end;
2087   //agrega el token final
2088   setlength(toks, idx+1);  //recorta al tamaño necesario
2089   toks[idx].TokPos:=idx;
2090   toks[idx].txt := GetToken;
2091   toks[idx].TokTyp:=fTokenID;
2092   toks[idx].posIni:=PosIni;
2093   toks[idx].curBlk := TopCodeFoldBlock(0);  //lee el rango
2094   Inc(idx);  //actualiza tamaño
2095   //verifica si lo ubicó.
2096   if CurTok = -1 then begin
2097     //No lo ubicó. Está más allá del fin de línea
2098     CurTok := idx-1;  //devuelve índice del token
2099     Result := TRUE;
2100   end;
2101 end;
TSynFacilSyn.SearchBeginBlocknull2102 function TSynFacilSyn.SearchBeginBlock(level: integer; PosY: integer): integer;
2103 //Busca en la linea "PosY", el inicio del bloque con nivel "level". Si no lo encuentra
2104 //en esa línea, devuelve -1, indicando que debe estar en la línea anterior.
2105 var
2106   niv1, niv2: Integer;   //niveles anterior y posterior
2107   ultApert : integer;    //posición de última apertura
2108 begin
2109   ultApert := -1; //valor por defecto
2110   niv1 := NestedBlocksBegin(PosY); //Verifica el nivel al inicio de la línea
2111   //explora línea
2112   StartAtLineIndex(PosY);   //posiciona y hace el primer Next()
2113   while not GetEol do begin
2114     niv2 := NestedBlocks;   //lee nivel después de hacer Next()
2115     if (niv1 < level) and (niv2>=level) then begin
2116       ultApert:= posIni+1;   //último cambio de nivel que incluye al nivel pedido (posición inicial)
2117     end;
2118     niv1 := niv2;
2119     Next;
2120   end;
2121   //Terminó de explorar.
2122   Result := ultApert;
2123 end;
TSynFacilSyn.SearchEndBlocknull2124 function TSynFacilSyn.SearchEndBlock(level: integer; PosY: integer): integer;
2125 //Busca en la linea "PosY", el fin del bloque con nivel "level". Si no lo encuentra
2126 //en esa línea, devuelve MAXINT, indicando que debe estar en la línea siguiente.
2127 var
2128   niv1, niv2: Integer;   //niveles anterior y posterior
2129 begin
2130   Result := MAXINT; //valor por defecto
2131   niv1 := NestedBlocksBegin(PosY); //Verifica el nivel al inicio de la línea
2132   //explora línea
2133   StartAtLineIndex(PosY);   //posiciona y hace el primer Next()
2134   while not GetEol do begin
2135     niv2 := NestedBlocks;   //lee nivel después de hacer Next()
2136     if (niv1 >= level) and (niv2 < level) then begin
2137       Result := posIni+1; //cambio de nivel que incluye al nivel pedido
2138       exit;       //ya tiene los datos requeridos
2139     end;
2140     niv1 := niv2;
2141     Next;
2142   end;
2143   //Terminó de explorar y no encontró el cierre de blqoue.
2144   //hace la verificación del último token
2145   niv2 := NestedBlocks;   //lee nivel después del último Next()
2146   if (niv1 >= level) and (niv2 < level) then begin
2147     Result:= posIni+1; //cambio de nivel que incluye al nivel pedido
2148     exit;       //ya tiene los datos requeridos
2149   end;
2150   //Ya verificó el último token y no encontró el cierre. Sale com MAXINT
2151 end;
2152 procedure TSynFacilSyn.SearchBeginEndBlock(level: integer; PosX, PosY: integer;
2153                                       out pIniBlock, pEndBlock: integer);
2154 //Explora una línea y devuelve el punto en la línea en que se abre y cierra el bloque de la
2155 //posición PosX. "level" debe indicar el nivel del bloque buscado.
2156 //Si no encuentra el inicio del bloque en la línea, devuelve -1
2157 var
2158   niv1, niv2: Integer;   //niveles anterior y posterior
2159   Despues: boolean;      //bandera para indicar si se alcanzó al token
2160 begin
2161   pIniBlock := -1; //valor por defecto
2162   pEndBlock := MAXINT;  //valor por defecto
2163   Despues := false;
2164   niv1 := NestedBlocksBegin(PosY); //Verifica el nivel al inicio de la línea
2165   //explora línea
2166   StartAtLineIndex(PosY);   //posiciona y hace el primer Next()
2167   while not GetEol do begin
2168     niv2 := NestedBlocks;   //lee nivel después de hacer Next()
2169     //verifica cambio de nivel
2170     if Despues then begin  //ya pasó al token actual
2171       if (niv1 >= level) and (niv2 < level) then begin
2172         pEndBlock:= posIni+1; //cambio de nivel que incluye al nivel pedido
2173         exit;       //ya tiene los datos requeridos
2174       end;
2175     end else begin        //aún no pasa al token actual
2176       if (niv1 < level) and (niv2>=level) then begin
2177         pIniBlock:= posIni+1;   //último cambio de nivel que incluye al nivel pedido (posición inicial)
2178       end;
2179     end;
2180     //verifica
2181     if (PosX >= posIni + 1) and (PosX < posFin+1) then begin
2182       //llegó a la posición pedida
2183       Despues := true;
2184 //      exit;    //Sale con el último "pIniBlock"
2185     end;
2186     niv1 := niv2;
2187     Next;
2188   end;
2189   //terminó de explorar la línea y no encontró el cierre de bloque
2190   if Despues then begin  //ya pasó al token actual, pero no encontró el cierre
2191     //hace la verificación del último token
2192     niv2 := NestedBlocks;   //lee nivel después del último Next()
2193     if (niv1 >= level) and (niv2 < level) then begin
2194       pEndBlock:= posIni+1; //cambio de nivel que incluye al nivel pedido
2195       exit;       //ya tiene los datos requeridos
2196     end;
2197   end else begin      //aún no pasa al token actual
2198     //No lo ubicó. PosX está más allá del fin de línea. Sale con el último "pIniBlock"
2199   end;
2200 end;
2201 procedure TSynFacilSyn.GetBlockInfoAtXY(XY: TPoint; out blk: TFaSynBlock;
2202                                         out level: integer);
2203 //Da información sobre el bloque en la posición indicada.
2204 begin
2205   SetHighlighterAtXY(XY);        //posiciona
2206   blk := TopBlock();  //lee bloque
2207   //level := CodeFoldRange.CodeFoldStackSize; no considera los que tienen IncreaseLevel=FALSE
2208   level := NestedBlocks;
2209 end;
TSynFacilSyn.GetBlockInfoAtXYnull2210 function TSynFacilSyn.GetBlockInfoAtXY(XY: TPoint; out blk: TFaSynBlock;
2211   out BlockStart: TPoint; out BlockEnd: TPoint): boolean;
2212 //Da información sobre el bloque en la posición indicada. Si hay error devuelve FALSE.
2213 //BlockStart y BlockEnd, tienen sus coordenadas empezando en 1.
2214 var
2215   nivel: Integer;
2216 //  PosY: integer;
2217 begin
2218   Result := SetHighlighterAtXY(XY);        //posiciona
2219   if Result=false then begin  //hubo error
2220     blk := nil;
2221     exit;
2222   end;
2223   blk := TopBlock();  //lee bloque
2224   //busca coordenadas del bloque
2225   nivel := NestedBlocks;   //ve el nivel actual
2226   BlockStart.y := XY.y;
2227   BlockEnd.y := XY.y;
2228   SearchBeginEndBlock(nivel, XY.x, BlockStart.y-1,BlockStart.x, BlockEnd.x);
2229   //busca posición de inicio
2230   if BlockStart.x = -1 then begin  //no se encontró en la línea actual
2231     while (BlockStart.y>1) do begin
2232       Dec(BlockStart.y);  //busca en la anterior
2233       BlockStart.x := SearchBeginBlock(nivel, BlockStart.y-1);
2234       if BlockStart.x<>-1 then break;  //encontró
2235     end;
2236   end;
2237   //busca posición de fin de bloque
2238   if BlockEnd.x = MAXINT then begin  //no se encontró en la línea actual
2239     while (BlockEnd.y < CurrentLines.Count) do begin
2240       Inc(BlockEnd.y);  //busca en la anterior
2241       BlockEnd.x := SearchEndBlock(nivel, BlockEnd.y-1);
2242       if BlockEnd.x<>MAXINT then break;  //encontró
2243     end;
2244 //    if BlockEnd.x = MAXINT then   //llegó al final, y no encontró el final
2245 //      BlockEnd.x := length(CurrentLines[BlockEnd.y-1])+1;
2246   end;
2247 end;
2248 //Utilidades para analizadores léxicos
TSynFacilSyn.GetXnull2249 function TSynFacilSyn.GetX: Integer; inline;
2250 begin
2251   Result:=posIni+1;  //corrige
2252 end;
GetYnull2253 function TSynFacilSyn.GetY: Integer; inline;
2254 begin
2255   Result:=lineIndex+1;  //corrige
2256 end;
TSynFacilSyn.GetXYnull2257 function TSynFacilSyn.GetXY: TPoint;
2258 //Devuelve las coordenadas de la posicón actual de exploración.
2259 //El inicio del texto inicia en (1,1)
2260 begin
2261   Result.x:=posIni+1;  //corrige
2262 end;
TSynFacilSyn.GetStatenull2263 function TSynFacilSyn.GetState: TFaLexerState;
2264 //Devuelve el estado actual del resaltador, pero sin considerar el estado de los bloque,
2265 //solo el estado de tokens y rangos.
2266 { TODO : Tal vez deba incluirse también a "FIsInNextToEOL" }
2267 begin
2268   //Propiedades fijadas al inicio de la línea y no cambian en toda la línea.
2269 //  Result.fLine    := fLine;
2270 //  Result.tamLin   := tamLin;
2271   Result.LineText := fLine;  //copia cadena indirectamente, porque "LineText", no es accesible
2272   Result.LineIndex:= LineIndex;  //define a la posición vertical
2273   //propiedades que van cambiando conforme se avanza en la exploración de la línea
2274   Result.posTok   := posTok;
2275   Result.BlkToClose:= CloseThisBlk;
2276   Result.posIni   := posIni;   //define la posición horizontal
2277   Result.posFin   := posFin;
2278   Result.fRange   := fRange;
2279   Result.fTokenID := fTokenID;
2280 end;
2281 procedure TSynFacilSyn.SetState(state: TFaLexerState);
2282 //Configura el estado actual del resaltador, pero sin considerar el estado de los bloque,
2283 //solo el estado de tokens y rangos.
2284 //Al cambiar el estado actual del resaltador, se pierde el estado que tenía.
2285 begin
2286   //Propiedades fijadas al inicio de la línea y no cambian en toda la línea.
2287   //fLine      := state.fLine;
2288   //tamLin     := state.tamLin;
2289   //LineText   := state.LineText;
2290   //LineIndex  := state.LineIndex;
2291   Setline(state.LineText, state.LineIndex);   {Como "LineText" y "LineIndex" no son
2292            accesibles, se usa SetLine(), y de paso se actualiza "fLine" y "tamLin"}
2293   //propiedades que van cambiando conforme se avanza en la exploración de la línea
2294   posTok     := state.posTok;
2295   CloseThisBlk := state.BlkToClose;
2296   posIni     := state.posIni;
2297   posFin     := state.posFin;
2298   fRange     := state.fRange;
2299   fTokenID   := state.fTokenID;
2300 end;
2301 
2302 // ******************** métodos OVERRIDE ********************//
2303 procedure TSynFacilSyn.SetLine(const NewValue: String; LineNumber: Integer);
2304 {Es llamado por el editor, cada vez que necesita actualizar la información de coloreado
2305  sobre una línea. Despues de llamar a esta función, se espera que GetTokenEx, devuelva
2306  el token actual. Y también después de cada llamada a "Next".}
2307 begin
2308   inherited;
2309   fLine := PChar(NewValue); //solo copia la dirección para optimizar
2310 //debugln('SetLine('+ IntToStr(LineNumber)+'): ' + fLine );
2311   tamLin := length(NewValue);
2312   posTok := 0;  //inicia contador
2313   posFin := 0;  //apunta al primer caracter
2314   CloseThisBlk := nil;   //inicia bandera
2315   Next;
2316 end;
2317 procedure TSynFacilSyn.Next;
2318 {Es llamado por SynEdit, para acceder al siguiente Token. Y es ejecutado por cada token de
2319  la línea en curso.
2320  En nuestro caso "posIni" debe quedar apuntando al inicio del token y "posFin" debe
2321  quedar apuntando al inicio del siguiente token o al caracter NULL (fin de línea).}
2322 begin
2323   //verifica si hay cerrado de bloque pendiente del token anterior
2324   if CloseThisBlk<>nil then begin
2325     EndBlockFa(CloseThisBlk);
2326     if CloseThisBlk.CloseParent then begin
2327       //debe cerrar también al padre
2328       EndBlockFa(TopBlock);
2329     end;
2330     CloseThisBlk := nil;
2331   end;
2332   Inc(posTok);  //lleva la cuenta del orden del token
2333 //  if posTok=1 then begin
2334 //    if OnFirstTok<>nil then OnFirstTok;
2335 //  end;
2336 
2337   posIni := posFin;   //apunta al primer elemento
2338   if fRange = nil then begin
2339       charIni:=fLine[posFin]; //guardar para tenerlo disponible en el método que se va a llamar.
2340       fProcTable[charIni];    //Se ejecuta la función que corresponda.
2341   end else begin
2342     if posFin = tamLin then begin  //para acelerar la exploración
2343       fTokenID:=tnEol;
2344       exit;
2345     end;
2346     {Debe actualizar el estado del rango porque las líneas no necesariamente se exploran
2347      consecutivamente}
2348     fTokenID:=fRange^.tTok;  //tipo de token
2349     delTok := fRange^.dEnd;  //delimitador de rango
2350     folTok := fRange^.folTok; //bandera para cerrar plegado
2351     fRange^.pRange;   //ejecuta método
2352   end;
2353 end;
GetEolnull2354 function TSynFacilSyn.GetEol: Boolean;
2355 begin
2356   Result := fTokenId = tnEol;
2357 end;
2358 procedure TSynFacilSyn.GetTokenEx(out TokenStart: PChar; out TokenLength: integer);
2359 begin
2360   TokenLength := posFin - posIni;
2361   TokenStart := FLine + posIni;
2362 end;
TSynFacilSyn.GetTokenAttributenull2363 function TSynFacilSyn.GetTokenAttribute: TSynHighlighterAttributes;
2364 {Debe devolver el atributo para el token actual. El token actual se actualiza con
2365  cada llamada a "Next", (o a "SetLine", para el primer token de la línea.)
2366  Esta función es la que usa SynEdit para definir el atributo del token actual}
2367 var topblk: TFaSynBlock;
2368 begin
2369   Result := Attrib[fTokenID];  //podría devolver "tkEol"
2370   if Result<> nil then begin
2371     //verifica coloreado de bloques
2372     case ColBlock of
2373     cbLevel: begin  //pinta por nivel
2374         Result.Background:=RGB(255- CodeFoldRange.CodeFoldStackSize*25,255- CodeFoldRange.CodeFoldStackSize*25,255);
2375       end;
2376     cbBlock: begin  //pinta por tipo de bloque
2377         topblk := TopBlockOpac;  //bloque con color
2378         //asigna color
2379         Result.Background:=topblk.BackCol;
2380       end;
2381     end;
2382   end;
2383 end;
TSynFacilSyn.GetDefaultAttributenull2384 function TSynFacilSyn.GetDefaultAttribute(Index: integer): TSynHighlighterAttributes;
2385 {Este método es llamado por la clase "TSynCustomHighlighter", cuando se accede a alguna de
2386  sus propiedades:  CommentAttribute, IdentifierAttribute, KeywordAttribute, StringAttribute,
2387  SymbolAttribute o WhitespaceAttribute.}
2388 begin
2389   case Index of
2390     SYN_ATTR_COMMENT   : Result := tkComment;
2391     SYN_ATTR_IDENTIFIER: Result := tkIdentif;
2392     SYN_ATTR_KEYWORD   : Result := tkKeyword;
2393     SYN_ATTR_WHITESPACE: Result := tkSpace;
2394     SYN_ATTR_STRING    : Result := tkString;
2395     SYN_ATTR_SYMBOL    : Result := tkSymbol;
2396     else Result := nil;
2397   end;
2398 end;
TSynFacilSyn.GetTokennull2399 function TSynFacilSyn.GetToken: String;
2400 var
2401   Len: LongInt;
2402 begin
2403   Len := posFin - posIni;
2404   SetString(Result, (FLine + posIni), Len);
2405 end;
GetTokenPosnull2406 function TSynFacilSyn.GetTokenPos: Integer;
2407 begin
2408   Result := posIni;
2409 end;
TSynFacilSyn.GetTokenKindnull2410 function TSynFacilSyn.GetTokenKind: integer;
2411 begin
2412   Result := fTokenId;
2413 end;
2414 {Implementación de las funcionalidades de rango}
2415 procedure TSynFacilSyn.ResetRange;
2416 begin
2417   inherited;
2418   fRange := nil;
2419 end;
GetRangenull2420 function TSynFacilSyn.GetRange: Pointer;
2421 begin
2422   CodeFoldRange.RangeType := fRange;
2423   Result := inherited GetRange;
2424   //debugln('  GetRange: ' + fLine + '=' + IntToStr(Integer(fRange)) );
2425 end;
2426 procedure TSynFacilSyn.SetRange(Value: Pointer);
2427 begin
2428 //debugln(' >SetRange: ' + fLine + '=' + IntToStr(PtrUInt(Value)) );
2429   inherited SetRange(Value);
2430   fRange := CodeFoldRange.RangeType;
2431 end;
2432 constructor TSynFacilSyn.Create(AOwner: TComponent);
2433 begin
2434   inherited Create(AOwner);
2435   lisTmp := TStringList.Create;   //crea lista temporal
2436   tc1:= tFaTokContent.Create;
2437   tc2:= tFaTokContent.Create;
2438   tc3:= tFaTokContent.Create;
2439   tc4:= tFaTokContent.Create;
2440 
2441   CaseSensitive := false;
2442   fRange := nil;     //inicia rango
2443 
2444   ClearSpecials;     //Inicia matrices
2445   CreateAttributes;  //crea los atributos
2446   lisBlocks:=TFaListBlocks.Create(true);  //crea lista de bloques con control
2447   //Crea bloque global
2448   MainBlk   := TFaSynBlock.Create;
2449   MainBlk.name:='Main';   //Nombre especial
2450   MainBlk.index:=-1;
2451   MainBlk.showFold:=false;
2452   MainBlk.parentBlk:=nil;  //no tiene ningún padre
2453   MainBlk.BackCol:=clNone;
2454   MainBlk.UniqSec:=false;
2455   MainBlk.CloseParent:=false;  //No tiene sentido porque este bloque no tiene padre
2456   //Crea bloque para tokens multilínea
2457   MulTokBlk  := TFaSynBlock.Create;
2458   MulTokBlk.name:='MultiToken';
2459   MulTokBlk.index:=-2;
2460   MulTokBlk.showFold:=true;  //Dejar en TRUE, porque así trabaja
2461   MulTokBlk.parentBlk:=nil;
2462   MulTokBlk.BackCol:=clNone;
2463   MulTokBlk.UniqSec:=false;
2464   MulTokBlk.CloseParent:=false;
2465 
2466   ClearMethodTables;   //Crea tabla de funciones
2467   DefTokIdentif('[A-Za-z$_]','[A-Za-z0-9_]*');
2468 end;
2469 destructor TSynFacilSyn.Destroy;
2470 begin
2471   MulTokBlk.Free;
2472   MainBlk.Free;
2473   lisBlocks.Destroy;        //libera
2474   tc1.Destroy;
2475   tc2.Destroy;
2476   tc3.Destroy;
2477   tc4.Destroy;
2478   lisTmp.Destroy;
2479   //no es necesario destruir los attrributos, porque  la clase ya lo hace
2480   inherited Destroy;
2481 end;
2482 
2483 end.
2484 
2485