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