1 {                               SynFacilBasic
2 Unidad con rutinas básicas de SynFacilSyn.
3 Incluye la definición de la clase base: TSynFacilSynBase, que es la clase padre
4 de TSYnFacilSyn.
5 Además icnluye la definición del tipo "tFaTokContent" y el procesamiento de
6 expresiones regulares que son usadas por TSynFacilSyn.
7 
8                                  Por Tito Hinostroza  02/12/2014 - Lima Perú
9 }
10 unit SynFacilBasic;
11 {$mode objfpc}{$H+}
12 interface
13 uses
14   SysUtils, Classes, SynEditHighlighter, strutils, Graphics, DOM, LCLIntf,
15   LCLProc, SynEditHighlighterFoldBase, SynEditTypes;
16 
17 type
18   ///////// Definiciones para manejo de tokens por contenido ///////////
19 
20   //Tipo de expresión regular soportada. Las exp. regulares soportadas son
21   //simples. Solo incluyen literales de cadena o listas.
22   tFaRegExpType = (
23     tregTokPos,   //Posición de token
24     tregString,   //Literal de cadena: "casa"
25     tregChars,    //Lista de caracteres: [A-Z]
26     tregChars01,  //Lista de caracteres: [A-Z]?
27     tregChars0_,  //Lista de caracteres: [A-Z]*
28     tregChars1_   //Lista de caracteres: [A-Z]+
29   );
30 
31   //Acciones a ejecutar en las comparaciones
32   tFaActionOnMatch = (
33     aomNext,    //pasa a la siguiente instrucción
34     aomExit,    //termina la exploración
35     aomMovePar, //Se mueve a una posición específica
36     aomExitpar  //termina la exploración retomando una posición específica.
37   );
38 
39   //Estructura para almacenar una instrucción de token por contenido
40   tFaTokContentInst = record
41     Chars    : array[#0..#255] of ByteBool; //caracteres
42     Text     : string;             //cadena válida
43     tokPos   : integer;  //Cuando se usa posición del token
44     expTyp   : tFaRegExpType;      //tipo de expresión
45     aMatch   : integer;  //atributo asignado en caso TRUE
46     aFail    : integer;  //atributo asignado en caso TRUE
47     //Campos para ejecutar instrucciones, cuando No cumple
48     actionFail : tFaActionOnMatch;
49     destOnFail : integer;  //posición destino
50     //Campos para ejecutar instrucciones, cuando cumple
51     actionMatch: tFaActionOnMatch;
52     destOnMatch: integer;  //posición destino
53 
54     posFin     : integer;  //para guardar posición
55   end;
56   tFaTokContentInstPtr = ^tFaTokContentInst;
57 
58   ESynFacilSyn = class(Exception);   //excepción del resaltador
59 
60   { tFaTokContent }
61   //Estructura para almacenar la descripción de los token por contenido
62   tFaTokContent = class
63     TokTyp   : integer;        //tipo de token por contenido
64     CaseSensitive: boolean;     //Usado para comparación de literales de cadena
65     Instrucs : array of tFaTokContentInst;  //Instrucciones del token por contenido
66     nInstruc : integer;      //Cantidad de instrucciones
67     procedure Clear;
68     procedure AddInstruct(exp: string; ifTrue: string = ''; ifFalse: string = '';
69       atMatch: integer = - 1; atFail: integer = - 1);
70     procedure AddRegEx(exp: string; Complete: boolean=false);
71   private
AddItemnull72     function AddItem(expTyp: tFaRegExpType; ifMatch, ifFail: string): integer;
73     procedure AddOneInstruct(var exp: string; ifTrue: string; ifFalse: string;
74       atMatch: integer = -1; atFail: integer = -1);
75   end;
76 
77   ///////// Definiciones básicas para el resaltador ///////////
78 
79   //Identifica si un token es el delimitador inicial
80   TFaTypeDelim =(tdNull,     //no es delimitado
81                  tdUniLin,   //es delimitador inicial de token delimitado de una línea
82                  tdMulLin,   //es delimitador inicial de token delimitado multilínea
83                  tdConten1,  //es delimitador inicial de token por contenido 1
84                  tdConten2,  //es delimitador inicial de token por contenido 2
85                  tdConten3,  //es delimitador inicial de token por contenido 3
86                  tdConten4); //es delimitador inicial de token por contenido 4
87   //Tipos de coloreado de bloques
88   TFaColBlock = (cbNull,     //sin coloreado
89                  cbLevel,    //colorea bloques por nivel
90                  cbBlock);   //colorea bloques usando el color definido para cada bloque
91 
92   TFaProcMetTable = procedure of object;   //Tipo de procedimiento para procesar el token de
93                                          //acuerdo al caracter inicial.
94   TFaProcRange = procedure of object;      //Procedimiento para procesar en medio de un rango.
95 
96   TFaSynBlock = class;   //definición adelantada
97 
98   //Descripción de tokens especiales (identificador o símbolo)
99   TTokSpec = record
100     txt   : string;        //palabra clave (puede cambiar la caja y no incluir el primer caracter)
101     orig  : string;        //palabra clave tal cual se indica
102     TokPos: integer;       //posición del token dentro de la línea
103     tTok  : integer;       //tipo de token
104     typDel: TFaTypeDelim;  {indica si el token especial actual, es en realidad, el
105                             delimitador inicial de un token delimitado o por contenido}
106     dEnd  : string;        //delimitador final (en caso de que sea delimitador)
107     pRange: TFaProcRange;  //procedimiento para procesar el token o rango(si es multilinea)
108     folTok: boolean;       //indica si el token delimitado, tiene plegado
109     chrEsc: char;          //Caracter de escape de token delimitado. Si no se usa, contiene #0.
110     //propiedades para manejo de bloques y plegado de código
111     openBlk   : boolean;      //indica si el token es inicio de bloque de plegado
112     BlksToOpen: array of TFaSynBlock;  //lista de referencias a los bloques que abre
113     closeBlk  : boolean;      //indica si el token es fin de bloque de plegado
114     BlksToClose: array of TFaSynBlock; //lista de referencias a los bloques que cierra
115     OpenSec   : boolean;      //indica si el token es inicio de sección de bloque
116     SecsToOpen: array of TFaSynBlock;  //lista de bloques de los que es inicio de sección
117     firstSec  : TFaSynBlock; //sección que se debe abrir al abrir el bloque
118   end;
119 
120   TEvBlockOnOpen = procedure(blk: TFaSynBlock; var Cancel: boolean) of object;
121 
122   TArrayTokSpec = array of TTokSpec;
123   //clase para manejar la definición de bloques de sintaxis
124   TFaSynBlock = class
125     name        : string;    //nombre del bloque
126     index       : integer;   //indica su posición dentro de TFaListBlocks
127     showFold    : boolean;   //indica si se mostrará la marca de plegado
128     parentBlk   : TFaSynBlock; //bloque padre (donde es válido el bloque)
129     BackCol     : TColor;    //color de fondo de un bloque
130     IsSection   : boolean;   //indica si es un bloque de tipo sección
131     UniqSec     : boolean;   //índica que es sección única
132     CloseParent : boolean;   //indica que debe cerrar al blqoue padre al cerrarse
133     OnBeforeOpen      : TEvBlockOnOpen;  //evento de apertura de bloque
134     OnBeforeClose     : TEvBlockOnOpen;  //evento de cierre de bloque
135   end;
136 
137   TPtrATokEspec = ^TArrayTokSpec;     //puntero a tabla
138   TPtrTokEspec = ^TTokSpec;     //puntero a tabla
139 
140   //Guarda información sobre un atributo de un nodo XML
141   TFaXMLatrib = record  //atributo XML
142     hay: boolean;    //bandera de existencia
143     val: string;     //valor en cadena
144     n  : integer;    //valor numérico
145     bol: boolean;    //valor booleando (si aplica)
146     col: TColor;     //valor de color (si aplica)
147   end;
148 
149   { TSynFacilSynBase }
150   //Clase con métodos básicos para el resaltador
151   TSynFacilSynBase = class(TSynCustomFoldHighlighter)
152   protected
153     fLine      : PChar;         //Puntero a línea de trabajo
154     tamLin     : integer;       //Tamaño de línea actual
155     fProcTable : array[#0..#255] of TFaProcMetTable;   //tabla de métodos
156     fAtriTable : array[#0..#255] of integer;   //tabla de atributos de tokens
157     posIni     : Integer;       //índice a inicio de token
158     posFin     : Integer;       //índice a siguiente token
159     fStringLen : Integer;       //Tamaño del token actual
160     fToIdent   : PChar;         //Puntero a identificador
161     fTokenID   : integer;      //Id del token actual
162     charIni    : char;          //caracter al que apunta fLine[posFin]
163     posTok     : integer;       //para identificar el ordinal del token en una línea
164 
165     CaseSensitive: boolean;     //Para ignorar mayúscula/minúscula
166     charsIniIden: Set of char;  //caracteres iniciales de identificador
167     lisTmp     : TStringList;   //lista temporal
168     fSampleSource: string;      //código de muestra
GetSampleSourcenull169     function GetSampleSource: String; override;
170   protected   //identificadores especiales
171     CharsIdentif: array[#0..#255] of ByteBool; //caracteres válidos para identificadores
172     tc1, tc2, tc3, tc4: tFaTokContent;
173     //Tablas para identificadores especiales
174     mA, mB, mC, mD, mE, mF, mG, mH, mI, mJ,
175     mK, mL, mM, mN, mO, mP, mQ, mR, mS, mT,
176     mU, mV, mW, mX, mY, mZ:  TArrayTokSpec;  //para mayúsculas
177     mA_,mB_,mC_,mD_,mE_,mF_,mG_,mH_,mI_,mJ_,
178     mK_,mL_,mM_,mN_,mO_,mP_,mQ_,mR_,mS_,mT_,
179     mU_,mV_,mW_,mX_,mY_,mZ_:  TArrayTokSpec;  //para minúsculas
180     m_, mDol, mArr, mPer, mAmp, mC3 : TArrayTokSpec;
181     mSym        :  TArrayTokSpec;   //tabla de símbolos especiales
182     mSym0       :  TArrayTokSpec;   //tabla temporal para símbolos especiales.
183     TabMayusc   : array[#0..#255] of Char;     //Tabla para conversiones rápidas a mayúscula
184   protected  //funciones básicas
BuscTokEspecnull185     function BuscTokEspec(var mat: TArrayTokSpec; cad: string; out n: integer;
186       TokPos: integer = 0): boolean;
ToListRegexnull187     function ToListRegex(list: TFaXMLatrib): string;
dStartRegexnull188     function dStartRegex(tStart, tCharsStart: TFaXMLatrib): string;
189     procedure VerifDelim(delim: string);
190     procedure ValidAsigDelim(delAct, delNue: TFaTypeDelim; delim: string);
191     procedure ValidateParamStart(Start: string; var ListElem: TStringList);
KeyCompnull192     function KeyComp(var r: TTokSpec): Boolean;
CreaBuscTokEspecnull193     function CreaBuscTokEspec(var mat: TArrayTokSpec; cad: string; out i: integer;
194       TokPos: integer = 0): boolean;
195     //procesamiento de XML
196     procedure CheckXMLParams(n: TDOMNode; listAtrib: string);
ReadXMLParamnull197     function ReadXMLParam(n: TDOMNode; nomb: string): TFaXMLatrib;
198   protected   //Métodos para tokens por contenido
199     procedure metTokCont(const tc: tFaTokContent); //inline;
200     procedure metTokCont1;
201     procedure metTokCont2;
202     procedure metTokCont3;
203     procedure metTokCont4;
204   protected  //Procesamiento de otros elementos
205     procedure metIdent;
206     procedure metIdentUTF8;
207     procedure metNull;
208     procedure metSpace;
209     procedure metSymbol;
210   public     //Funciones públicas
211     procedure DefTokIdentif(dStart, Content: string );
212   public     //Atributos y sus propiedades de acceso
213     //Atributos predefinidos
214     tkEol     : TSynHighlighterAttributes;
215     tkSymbol  : TSynHighlighterAttributes;
216     tkSpace   : TSynHighlighterAttributes;
217     tkIdentif : TSynHighlighterAttributes;
218     tkNumber  : TSynHighlighterAttributes;
219     tkKeyword : TSynHighlighterAttributes;
220     tkString  : TSynHighlighterAttributes;
221     tkComment : TSynHighlighterAttributes;
222     //ID para los tokens
223     tnEol     : integer;  //id para los tokens salto de línea
224     tnSymbol  : integer;  //id para los símbolos
225     tnSpace   : integer;  //id para los espacios
226     tnIdentif : integer;  //id para los identificadores
227     tnNumber  : integer;  //id para los números
228     tnKeyword : integer;  //id para las palabras claves
229     tnString  : integer;  //id para las cadenas
230     tnComment : integer;  //id para los comentarios
231     {Se crea el contenedor adicional Attrib[], para los atributos, porque aunque ya se
232     tiene Attribute[] en TSynCustomHighlighter, este está ordenado pro defecto y no
233     ayuda en ubicar a los attributos por su índice}
234     Attrib: array of TSynHighlighterAttributes;
NewTokAttribnull235     function NewTokAttrib(TypeName: string; out TokID: integer
236       ): TSynHighlighterAttributes;
NewTokTypenull237     function NewTokType(TypeName: string; out TokAttrib: TSynHighlighterAttributes
238       ): integer;
NewTokTypenull239     function NewTokType(TypeName: string): integer;
240     procedure CreateAttributes;  //limpia todos loa atributos
GetAttribByNamenull241     function GetAttribByName(txt: string): TSynHighlighterAttributes;
GetAttribIDByNamenull242     function GetAttribIDByName(txt: string): integer;
IsAttributeNamenull243     function IsAttributeName(txt: string): boolean;
244     protected
ProcXMLattributenull245     function ProcXMLattribute(nodo: TDOMNode): boolean;
246   public //Inicializacoón
247     constructor Create(AOwner: TComponent); override;
248   end;
249 
ExtractRegExpnull250 function ExtractRegExp(var exp: string; out str: string; out listChars: string): tFaRegExpType;
ExtractRegExpNnull251 function ExtractRegExpN(var exp: string; out RegexTyp: tFaRegExpType ): string;
ReplaceEscapenull252 function ReplaceEscape(str: string): string;
ColorFromStrnull253 function ColorFromStr(cad: string): TColor;
254 implementation
255 const
256     //Mensajes de error generales
257 //    ERR_START_NO_EMPTY = 'Parámetro "Start" No puede ser nulo';
258 //    ERR_EXP_MUST_BE_BR = 'Expresión debe ser de tipo [lista de caracteres]';
259 //    ERR_TOK_DELIM_NULL = 'Delimitador de token no puede ser nulo';
260 //    ERR_NOT_USE_START = 'No se puede usar "Start" y "CharsStart" simultáneamente.';
261 //    ERR_PAR_START_CHARS = 'Se debe definir el parámetro "Start" o "CharsStart".';
262 //    ERR_TOK_DEL_IDE_ERR = 'Delimitador de token erróneo: %s (debe ser identificador)';
263 //    ERR_IDEN_ALREA_DEL = 'Identificador "%s" ya es delimitador inicial.';
264 //    ERR_INVAL_ATTR_LAB = 'Atributo "%s" no válido para etiqueta <%s>';
265 //    ERR_BAD_PAR_STR_IDEN = 'Parámetro "Start" debe ser de la forma: "[A-Z]", en identificadores';
266 //    ERR_BAD_PAR_CON_IDEN = 'Parámetro "Content" debe ser de la forma: "[A-Z]*", en identificadores';
267 
268     ERR_START_NO_EMPTY = 'Parameter "Start" can not be null';
269     ERR_EXP_MUST_BE_BR = 'Expression must be like: [list of chars]';
270     ERR_TOK_DELIM_NULL = 'Token delimiter can not be null';
271     ERR_NOT_USE_START = 'Cannot use "Start" and "CharsStart" simultaneously.';
272     ERR_PAR_START_CHARS = 'It must be defined "Start" or "CharsStart" parameter.';
273     ERR_TOK_DEL_IDE_ERR = 'Bad Token delimiter: %s (must be identifier)';
274     ERR_IDEN_ALREA_DEL = 'Identifier "%s" is already a Start delimiter.';
275     ERR_INVAL_ATTR_LAB = 'Invalid attribute "%s" for label <%s>';
276     ERR_BAD_PAR_STR_IDEN = 'Parameter "Start" must be like: "[A-Z]", in identifiers';
277     ERR_BAD_PAR_CON_IDEN = 'Parameter "Content" must be like: "[A-Z]*", in identifiers';
278 
279     //Mensajes de tokens por contenido
280 //    ERR_EMPTY_INTERVAL = 'Error: Intervalo vacío.';
281 //    ERR_EMPTY_EXPRES = 'Expresión vacía.';
282 //    ERR_EXPECTED_BRACK = 'Se esperaba "]".';
283 //    ERR_UNSUPPOR_EXP_ = 'Expresión no soportada.';
284 //    ERR_INC_ESCAPE_SEQ = 'Secuencia de escape incompleta.';
285 //    ERR_SYN_PAR_IFFAIL_ = 'Error de sintaxis en parámetro "IfFail": ';
286 //    ERR_SYN_PAR_IFMATCH_ = 'Error de sintaxis en parámetro "IfMarch": ';
287     ERR_EMPTY_INTERVAL = 'Error: Empty Interval.';
288     ERR_EMPTY_EXPRES = 'Empty expression.';
289     ERR_EXPECTED_BRACK = 'Expected "]".';
290     ERR_UNSUPPOR_EXP_ = 'Unsupported expression: ';
291     ERR_INC_ESCAPE_SEQ = 'Incomplete Escape sequence';
292     ERR_SYN_PAR_IFFAIL_ = 'Syntax error on Parameter "IfFail": ';
293     ERR_SYN_PAR_IFMATCH_ = 'Syntax error on Parameter "IfMarch": ';
294 
295 var
296   bajos: string[128];
297   altos: string[128];
298 
copyExnull299 function copyEx(txt: string; p: integer): string;
300 //Versión sobrecargada de copy con 2 parámetros
301 begin
302   Result := copy(txt, p, length(txt));
303 end;
304 //Funciones para el manejo de expresiones regulares
ExtractCharnull305 function ExtractChar(var txt: string; out escaped: boolean; convert: boolean): string;
306 //Extrae un caracter de una expresión regular. Si el caracter es escapado, devuelve
307 //TRUE en "escaped"
308 //Si covert = TRUE, reemplaza el caracter compuesto por uno solo.
309 var
310   c: byte;
311 begin
312   escaped := false;
313   Result := '';   //valor por defecto
314   if txt = '' then exit;
315   if txt[1] = '\' then begin  //caracter escapado
316     escaped := true;
317     if length(txt) = 1 then  //verificación
318       raise ESynFacilSyn.Create(ERR_INC_ESCAPE_SEQ);
319     if txt[2] in ['x','X'] then begin
320       //caracter en hexadecimal
321       if length(txt) < 4 then  //verificación
322         raise ESynFacilSyn.Create(ERR_INC_ESCAPE_SEQ);
323       if convert then begin    //toma caracter hexdecimal
324         c := StrToInt('$'+copy(txt,3,2));
325         Result := Chr(c);
326       end else begin  //no tranforma
327         Result := copy(txt, 1,4);
328       end;
329       txt := copyEx(txt,5);
330     end else begin //se supone que es de tipo \A
331       //secuencia normal de dos caracteres
332       if convert then begin  //hay que convertirlo
333         Result := txt[2];
334       end else begin  //lo toma tal cual
335         Result := copy(txt,1,2);
336       end;
337       txt := copyEx(txt,3);
338     end;
339   end else begin   //caracter normal
340     Result := txt[1];
341     txt := copyEx(txt,2);
342   end;
343 end;
ExtractCharnull344 function ExtractChar(var txt: string): char;
345 //Versión simplificada de ExtractChar(). Extrae un caracter ya convertido. Si no hay
346 //más caracteres, devuelve #0
347 var
348   escaped: boolean;
349   tmp: String;
350 begin
351   if txt = '' then Result := #0
352   else begin
353     tmp := ExtractChar(txt, escaped, true);
354     Result := tmp[1];  //se supone que siempre será de un solo caracter
355   end;
356 end;
ExtractCharNnull357 function ExtractCharN(var txt: string): string;
358 //Versión simplificada de ExtractChar(). Extrae un caracter sin convertir.
359 var
360   escaped: boolean;
361 begin
362   Result := ExtractChar(txt, escaped, false);
363 end;
ReplaceEscapenull364 function ReplaceEscape(str: string): string;
365 {Reemplaza las secuencias de escape por su caracter real. Las secuencias de
366 escape recnocidas son:
367 * Secuencia de 2 caracteres: "\#", donde # es un caracter cualquiera, excepto"x".
368   Esta secuencia equivale al caracter "#".
369 * Secuencia de 4 caracteres: "\xHH" o "\XHH", donde "HH" es un número hexadecimnal.
370   Esta secuencia representa a un caracter ASCII.
371 
372 Dentro de las expresiones regulares de esta librería, los caracteres: "[", "*", "?",
373 "*", y "\", tienen significado especial, por eso deben "escaparse".
374 
375 "\\" -> "\"
376 "\[" -> "["
377 "\*" -> "*"
378 "\?" -> "?"
379 "\+" -> "+"
380 "\x$$" -> caracter ASCII $$
381 }
382 begin
383   Result := '';
384   while str<>'' do
385     Result += ExtractChar(str);
386 end;
EscapeTextnull387 function EscapeText(str: string): string;
388 //Comvierte los caracteres que pueden tener significado especial en secuencias de
389 //escape para que se procesen como caracteres normales.
390 begin
391   str := StringReplace(str, '\', '\\',[rfReplaceAll]);  //debe hacerse primero
392   str := StringReplace(str, '[', '\[',[rfReplaceAll]);
393   str := StringReplace(str, '*', '\*',[rfReplaceAll]);
394   str := StringReplace(str, '?', '\?',[rfReplaceAll]);
395   str := StringReplace(str, '+', '\+',[rfReplaceAll]);
396   Result := str;
397 end;
PosCharnull398 function PosChar(ch: char; txt: string): integer;
399 //Similar a Pos(). Devuelve la posición de un caracter que no este "escapado"
400 var
401   f: SizeInt;
402 begin
403   f := Pos(ch,txt);
404   if f=1 then exit(1);   //no hay ningún caracter antes.
405   while (f>0) and (txt[f-1]='\') do begin
406     f := PosEx(ch, txt, f+1);
407   end;
408   Result := f;
409 end;
ExtractRegExpnull410 function ExtractRegExp(var exp: string; out str: string; out listChars: string): tFaRegExpType;
411 {Extrae parte de una expresión regular y devuelve el tipo. Esta función se basa en
412 que toda expresión regular se puede reducir a literales de cadenas o listas (con o
413 sin cuantificador).
414 En los casos de listas de caracteres, expande los intervalos de tipo: A..Z, reemplaza
415 las secuencias de escape y devuelve la lista en "listChars".
416 En el caso de que sea un literal de cadena, reemplaza las secuencias de escape y
417 devuelve la cadena en "str".
418 Soporta todas las formas definidas en "tFaRegExpType".
419 Si encuentra error, genera una excepción.}
420   procedure ValidateInterval(var cars: string);
421   {Valida un conjunto de caracteres, expandiendo los intervalos de tipo "A-Z", y
422   remplazando las secuencias de escape como: "\[", "\\", "\-", ...
423   El caracter "-", se considera como indicador de intervalo, a menos que se encuentre
424   en el primer o ùltimo caracter de la cadena, o esté escapado.
425   Si hay error genera una excepción.}
426   var
427     c, car1, car2: char;
428     car: string;
429     tmp: String;
430     Invert: Boolean;
431     carsSet: set of char;
432   begin
433     //reemplaza intervalos
434     if cars = '' then
435       raise ESynFacilSyn.Create(ERR_EMPTY_INTERVAL);
436     //Verifica si es lista invertida
437     Invert := false;
438     if cars[1] = '^' then begin
439       Invert := true;        //marca
440       cars := copyEx(cars,2);  //quita "^"
441     end;
442     //Procesa contenido, reemplazando los caracteres escapados.
443     //Si el primer caracter es "-". lo toma literal, sin asumir error.
444     car1 := ExtractChar(cars);   //Extrae caracter convertido. Se asume que es inicio de intervalo.
445     tmp := car1;  //inicia cadena para acumular.
446     car := ExtractCharN(cars);   //Eextrae siguiente. Sin convertir porque puede ser "\-"
447     while car<>'' do begin
448       if car = '-' then begin
449         //es intervalo
450         car2 := ExtractChar(cars);   //caracter final
451         if car2 = #0 then begin
452           //Es intervalo incompleto, podría genera error, pero mejor asumimos que es el caracter "-"
453           tmp += '-';
454           break;  //sale por que se supone que ya no hay más caracteres
455         end;
456         //se tiene un intervalo que hay que reemplazar
457         for c := Chr(Ord(car1)+1) to car2 do  //No se incluye "car1", porque ya se agregó
458           tmp += c;
459       end else begin  //simplemente acumula
460         car1 := ExtractChar(car);   //Se asume que es inicio de intervalo. No importa perder "car"
461         tmp += car1;  //Es necesario, porque puede estar escapado
462       end;
463       car := ExtractCharN(cars);  //extrae siguiente
464     end;
465     cars := StringReplace(tmp, '%HIGH%', altos,[rfReplaceAll]);
466     cars := StringReplace(cars, '%ALL%', bajos+altos,[rfReplaceAll]);
467     //Verifica si debe invertir lista
468     if Invert then begin
469       //Convierte a conjunto
470       carsSet := [];
471       for c in cars do carsSet += [c];
472       //Agrega caracteres
473       cars := '';
474       for c := #1 to #255 do  //no considera #0
475         if not (c in carsSet) then cars += c;
476     end;
477   end;
478 var
479   tmp: string;
480   lastAd: String;
481 begin
482   if exp= '' then
483     raise ESynFacilSyn.Create(ERR_EMPTY_EXPRES);
484   //Verifica la forma TokPos=1
485   if UpCase(copy(exp,1,7)) = 'TOKPOS=' then begin
486     //Caso especial de la forma TokPos=n
487     str := copy(exp,8,2);  //Aquí se devuelve "n"
488     exp := '';    //ya no quedan caracteres
489     Result := tregTokPos;
490     exit;
491   end;
492   //Reemplaza secuencias conocidas que equivalen a listas.
493   if copy(exp,1,2) = '\d' then begin
494     exp := '[0-9]' + copyEx(exp,3);
495   end else if copy(exp,1,2) = '\D' then begin
496     exp := '[^0-9]' + copyEx(exp,3);
497   end else if copy(exp,1,2) = '\a' then begin
498     exp := '[A-Za-z]' + copyEx(exp,3);
499   end else if copy(exp,1,2) = '\w' then begin
500     exp := '[A-Za-z0-9_]' + copyEx(exp,3);
501   end else if copy(exp,1,2) = '\W' then begin
502     exp := '[^A-Za-z0-9_]' + copyEx(exp,3);
503   end else if copy(exp,1,2) = '\s' then begin
504     exp := ' ' + copyEx(exp,3);
505   end else if copy(exp,1,2) = '\S' then begin
506     exp := '[^ ]' + copyEx(exp,3);
507   end else if copy(exp,1,2) = '\t' then begin
508     exp := '\x09' + copyEx(exp,3);
509   end else if copy(exp,1,1) = '.' then begin
510     exp := '[\x01-\xFF]' + copyEx(exp,2);
511   end;
512   //analiza la secuencia
513   if (exp[1] = '[') and (length(exp)>1) then begin    //Es lista de caracteres
514     //Captura interior del intervalo.
515     exp := CopyEx(exp,2);
516     listChars := '';
517     tmp := ExtractCharN(exp);   //No convierte para no confundir "\]"
518     while (exp<>'') and (tmp<>']') do begin
519       listChars += tmp;
520       tmp := ExtractCharN(exp);  //No convierte para no confundir "\]"
521     end;
522     if (tmp<>']') then   //no se encontró ']'
523       raise ESynFacilSyn.Create(ERR_EXPECTED_BRACK);
524     //la norma es tener aquí, el contenido de la lista, pero manteniendo los caracteres escapados
525     ValidateInterval(listChars);  //puede simplificar "listChars". También puede generar excepción
526     if exp = '' then begin   //Lista de tipo "[ ... ]"
527       Result := tregChars;
528     end else if exp[1] = '*' then begin  //Lista de tipo "[ ... ]* ... "
529       exp := copyEx(exp,2);    //extrae parte procesada
530       Result := tregChars0_
531     end else if exp[1] = '?' then begin  //Lista de tipo "[ ... ]? ... "
532       exp := copyEx(exp,2);    //extrae parte procesada
533       Result := tregChars01
534     end else if exp[1] = '+' then begin  //Lista de tipo "[ ... ]+ ... "
535       exp := copyEx(exp,2);    //extrae parte procesada
536       Result := tregChars1_
537     end else begin
538       //No sigue ningún cuantificador, podrías er algún literal
539       Result := tregChars;  //Lista de tipo "[ ... ] ... "
540     end;
541   end else if (length(exp)=1) and (exp[1] in ['*','?','+','[']) then begin
542     //Caso especial, no se usa escape, pero no es lista, ni cuantificador. Se asume
543     //caracter único
544     listChars := exp;  //'['+exp+']'
545     exp := '';    //ya no quedan caracteres
546     Result := tregChars;
547     exit;
548   end else begin
549     //No inicia con lista. Se puede suponer que inicia con literal cadena.
550     {Pueden ser los casos:
551       Caso 0) "abc"    (solo literal cadena, se extraerá la cadena "abc")
552       Caso 1) "abc[ ... "  (válido, se extraerá la cadena "abc")
553       Caso 2) "a\[bc[ ... " (válido, se extraerá la cadena "a[bc")
554       Caso 3) "abc* ... "  (válido, pero se debe procesar primero "ab")
555       Caso 4) "ab\\+ ... " (válido, pero se debe procesar primero "ab")
556       Caso 5) "a? ... "    (válido, pero debe transformarse en lista)
557       Caso 6) "\[* ... "   (válido, pero debe transformarse en lista)
558     }
559     str := '';   //para acumular
560     tmp := ExtractCharN(exp);
561     lastAd := '';   //solo por seguridad
562     while tmp<>'' do begin
563       if tmp = '[' then begin
564         //Empieza una lista. Caso 1 o 2
565         exp:= '[' + exp;  //devuelve el caracter
566         str := ReplaceEscape(str);
567 {        if length(str) = 1 then begin  //verifica si tiene un caracter
568           listChars := str;       //'['+str+']'
569           Result := tregChars;   //devuelve como lista de un caracter
570           exit;
571         end;}
572         Result := tregString;   //es literal cadena
573         exit;  //sale con lo acumulado en "str"
574       end else if (tmp = '*') or (tmp = '?') or (tmp = '+') then begin
575         str := copy(str, 1, length(str)-length(lastAd)); //no considera el último caracter
576         if str <> '' then begin
577           //Hay literal cadena, antes de caracter y cuantificador. Caso 3 o 4
578           exp:= lastAd + tmp + exp;  //devuelve el último caracter agregado y el cuantificador
579           str := ReplaceEscape(str);
580           if length(str) = 1 then begin  //verifica si tiene un caracter
581             listChars := str;       //'['+str+']'
582             Result := tregChars;   //devuelve como lista de un caracter
583             exit;
584           end;
585           Result := tregString;   //es literal cadena
586           exit;
587         end else begin
588           //Hay caracter y cuantificador. . Caso 5 o 6
589           listChars := ReplaceEscape(lastAd);  //'['+lastAd+']'
590           //de "exp" ya se quitó: <caracter><cuantificador>
591           if          tmp = '*' then begin  //Lista de tipo "[a]* ... "
592             Result := tregChars0_
593           end else if tmp = '?' then begin  //Lista de tipo "[a]? ... "
594             Result := tregChars01
595           end else if tmp = '+' then begin  //Lista de tipo "[a]+ ... "
596             Result := tregChars1_
597           end;   //no hay otra opción
598           exit;
599         end;
600       end;
601       str += tmp;   //agrega caracter
602       lastAd := tmp;  //guarda el último caracter agregado
603       tmp := ExtractCharN(exp);  //siguiente caracter
604     end;
605     //Si llega aquí es porque no encontró cuantificador ni lista (Caso 0)
606     str := ReplaceEscape(str);
607 {    if length(str) = 1 then begin  //verifica si tiene un caracter
608       listChars := str;       //'['+str+']'
609       Result := tregChars;   //devuelve como lista de un caracter
610       exit;
611     end;}
612     Result := tregString;
613   end;
614 end;
ExtractRegExpNnull615 function ExtractRegExpN(var exp: string; out RegexTyp: tFaRegExpType): string;
616 {Extrae parte de una expresión regular y la devuelve como cadena . Actualiza el
617 tipo de expresión obtenida en "RegexTyp".
618 No Reemplaza las secuencias de excape ni los intervalos, devuelve el texto tal cual}
619 var
620   listChars, str: string;
621   exp0: String;
622   tam: Integer;
623 begin
624   exp0 := exp;   //guarda expresión tal cual
625   RegexTyp := ExtractRegExp(exp, str, listChars);
626   tam := length(exp0) - length(exp);  //ve diferencia de tamaño
627   Result := copy(exp0, 1, tam)
628 end;
ColorFromStrnull629 function ColorFromStr(cad: string): TColor;
630 //Convierte una cadena a Color
EsHexanull631   function EsHexa(txt: string; out num: integer): boolean;
632   //Convierte un texto en un número entero. Si es numérico devuelve TRUE
633   var i: integer;
634   begin
635     Result := true;  //valor por defecto
636     num := 0; //valor por defecto
637     for i:=1 to length(txt) do begin
638       if not (txt[i] in ['0'..'9','a'..'f','A'..'F']) then exit(false);  //no era
639     end;
640     //todos los dígitos son numéricos
641     num := StrToInt('$'+txt);
642   end;
643 var
644   r, g, b: integer;
645 begin
646   if (cad<>'') and (cad[1] = '#') and (length(cad)=7) then begin
647     //es código de color. Lo lee de la mejor forma
648     EsHexa(copy(cad,2,2),r);
649     EsHexa(copy(cad,4,2),g);
650     EsHexa(copy(cad,6,2),b);
651     Result:=RGB(r,g,b);
652   end else begin  //constantes de color
653     case UpCase(cad) of
654     'WHITE'      : Result :=rgb($FF,$FF,$FF);
655     'SILVER'     : Result :=rgb($C0,$C0,$C0);
656     'GRAY'       : Result :=rgb($80,$80,$80);
657     'BLACK'      : Result :=rgb($00,$00,$00);
658     'RED'        : Result :=rgb($FF,$00,$00);
659     'MAROON'     : Result :=rgb($80,$00,$00);
660     'YELLOW'     : Result :=rgb($FF,$FF,$00);
661     'OLIVE'      : Result :=rgb($80,$80,$00);
662     'LIME'       : Result :=rgb($00,$FF,$00);
663     'GREEN'      : Result :=rgb($00,$80,$00);
664     'AQUA'       : Result :=rgb($00,$FF,$FF);
665     'TEAL'       : Result :=rgb($00,$80,$80);
666     'BLUE'       : Result :=rgb($00,$00,$FF);
667     'NAVY'       : Result :=rgb($00,$00,$80);
668     'FUCHSIA'    : Result :=rgb($FF,$00,$FF);
669     'PURPLE'     : Result :=rgb($80,$00,$80);
670 
671     'MAGENTA'    : Result :=rgb($FF,$00,$FF);
672     'CYAN'       : Result :=rgb($00,$FF,$FF);
673     'BLUE VIOLET': Result :=rgb($8A,$2B,$E2);
674     'GOLD'       : Result :=rgb($FF,$D7,$00);
675     'BROWN'      : Result :=rgb($A5,$2A,$2A);
676     'CORAL'      : Result :=rgb($FF,$7F,$50);
677     'VIOLET'     : Result :=rgb($EE,$82,$EE);
678     end;
679   end;
680 end;
681 
682 { tFaTokContent }
683 procedure tFaTokContent.Clear;
684 begin
685   CaseSensitive := false;   //por defecto
686   nInstruc := 0;
687   setLength(Instrucs,0);
688 end;
tFaTokContent.AddItemnull689 function tFaTokContent.AddItem(expTyp: tFaRegExpType; ifMatch, ifFail: string): integer;
690 //Agrega un ítem a la lista Instrucs[]. Devuelve el número de ítems.
691 //Configura el comportamiento de la instrucción usando "ifMatch".
692 var
693   ifMatch0, ifFail0: string;
694 
extractInsnull695   function extractIns(var txt: string): string;
696   //Extrae una instrucción (identificador)
697   var
698     p: Integer;
699   begin
700     txt := trim(txt);
701     if txt = '' then exit('');
702     p := 1;
703     while (p<=length(txt)) and (txt[p] in ['A'..'Z']) do inc(p);
704     Result := copy(txt,1,p-1);
705     txt := copyEx(txt, p);
706 //    Result := copy(txt,1,p);
707 //    txt := copyEx(txt, p+1);
708   end;
extractParnull709   function extractPar(var txt: string; errMsg: string): integer;
710   //Extrae un valor numérico
711   var
712     p, p0: Integer;
713     sign: Integer;
714   begin
715     txt := trim(txt);
716     if txt = '' then exit(0);
717     if txt[1] = '(' then begin
718       //caso esperado
719       p := 2;  //explora
720       if not (txt[2] in ['+','-','0'..'9']) then  //validación
721         raise ESynFacilSyn.Create(errMsg + ifFail0);
722       sign := 1;  //signo por defecto
723       if txt[2] = '+' then begin
724         p := 3;  //siguiente caracter
725         sign := 1;
726         if not (txt[3] in ['0'..'9']) then
727           raise ESynFacilSyn.Create(errMsg + ifFail0);
728       end;
729       if txt[2] = '-' then begin
730         p := 3;  //siguiente caracter
731         sign := -1;
732         if not (txt[3] in ['0'..'9']) then
733           raise ESynFacilSyn.Create(errMsg + ifFail0);
734       end;
735       //Aquí se sabe que en txt[p], viene un númaro
736       p0 := p;   //guarda posición de inicio
737       while (p<=length(txt)) and (txt[p] in ['0'..'9']) do inc(p);
738       Result := StrToInt(copy(txt,p0,p-p0)) * Sign;  //lee como número
739       if txt[p]<>')' then raise ESynFacilSyn.Create(errMsg + ifFail0);
740       inc(p);
741       txt := copyEx(txt, p+1);
742     end else begin
743       raise ESynFacilSyn.Create(errMsg + ifFail0);
744     end;
745   end;
HaveParnull746   function HavePar(var txt: string): boolean;
747   //Verifica si la cadena empieza con "("
748   begin
749     Result := false;
750     txt := trim(txt);
751     if txt = '' then exit;
752     if txt[1] = '(' then begin   //caso esperado
753       Result := true;
754     end;
755   end;
756 
757 var
758   inst: String;
759   n: Integer;
760 begin
761   ifMatch0 := ifMatch;  //guarda valor original
762   ifFail0 := ifFail;    //guarda valor original
763   inc(nInstruc);
764   n := nInstruc-1;  //último índice
765   setlength(Instrucs, nInstruc);
766   Instrucs[n].expTyp := expTyp;    //tipo
767   Instrucs[n].actionMatch := aomNext;  //valor por defecto
768   Instrucs[n].actionFail  := aomExit; //valor por defecto
769   Instrucs[n].destOnMatch:=0;         //valor por defecto
770   Instrucs[n].destOnFail:= 0;         //valor por defecto
771   Result := nInstruc;
772   //Configura comportamiento
773   if ifMatch<>'' then begin
774     ifMatch := UpCase(ifMatch);
775     while ifMatch<>'' do begin
776       inst := extractIns(ifMatch);
777       if inst = 'NEXT' then begin  //se pide avanzar al siguiente
778         Instrucs[n].actionMatch := aomNext;
779       end else if inst = 'EXIT' then begin  //se pide salir
780         if HavePar(ifMatch) then begin  //EXIT con parámetro
781           Instrucs[n].actionMatch := aomExitpar;
782           Instrucs[n].destOnMatch := n + extractPar(ifMatch, ERR_SYN_PAR_IFMATCH_);
783         end else begin   //EXIT sin parámetros
784           Instrucs[n].actionMatch := aomExit;
785         end;
786       end else if inst = 'MOVE' then begin
787         Instrucs[n].actionMatch := aomMovePar;  //Mover a una posición
788         Instrucs[n].destOnMatch := n + extractPar(ifMatch, ERR_SYN_PAR_IFMATCH_);
789       end else begin
790         raise ESynFacilSyn.Create(ERR_SYN_PAR_IFMATCH_ + ifMatch0);
791       end;
792       ifMatch := Trim(ifMatch);
793       if (ifMatch<>'') and (ifMatch[1] = ';') then  //quita delimitador
794         ifMatch := copyEx(ifMatch,2);
795     end;
796   end;
797   if ifFail<>'' then begin
798     ifFail := UpCase(ifFail);
799     while ifFail<>'' do begin
800       inst := extractIns(ifFail);
801       if inst = 'NEXT' then begin  //se pide avanzar al siguiente
802         Instrucs[n].actionFail := aomNext;
803       end else if inst = 'EXIT' then begin  //se pide salir
804         if HavePar(ifFail) then begin  //EXIT con parámetro
805           Instrucs[n].actionFail := aomExitpar;
806           Instrucs[n].destOnFail := n + extractPar(ifFail, ERR_SYN_PAR_IFFAIL_);
807         end else begin   //EXIT sin parámetros
808           Instrucs[n].actionFail := aomExit;
809         end;
810       end else if inst = 'MOVE' then begin
811         Instrucs[n].actionFail := aomMovePar;  //Mover a una posición
812         Instrucs[n].destOnFail := n + extractPar(ifFail, ERR_SYN_PAR_IFFAIL_);
813       end else begin
814         raise ESynFacilSyn.Create(ERR_SYN_PAR_IFFAIL_ + ifFail0);
815       end;
816       ifFail := Trim(ifFail);
817       if (ifFail<>'') and (ifFail[1] = ';') then  //quita delimitador
818         ifFail := copyEx(ifFail,2);
819     end;
820   end;
821 end;
822 procedure tFaTokContent.AddOneInstruct(var exp: string; ifTrue: string; ifFalse: string;
823       atMatch: integer=-1; atFail: integer=-1);
824 {Agrega una y solo instrucción al token por contenido. Si encuentra más de una
825 instrucción, genera una excepción. Si se pone ifTrue en blnnco, se asumirá 'next',
826 si se pone "ifFalse" en blanco, se se asumirá 'exit'.
827 Este es el punto de entrada único para agregar una instrucción de Regex a
828 tFaTokContent}
829 var
830   list: String;
831   str: string;
832   n: Integer;
833   c: Char;
834   expr: string;
835   t: tFaRegExpType;
836 begin
837   if exp='' then exit;
838   //analiza
839   expr := exp;   //guarda, porque se va a trozar
840   t := ExtractRegExp(exp, str, list);
841   case t of
842   tregChars,    //Es de tipo lista de caracteres [...]
843   tregChars01,  //Es de tipo lista de caracteres [...]?
844   tregChars0_,  //Es de tipo lista de caracteres [...]*
845   tregChars1_:  //Es de tipo lista de caracteres [...]+
846     begin
847       n := AddItem(t, ifTrue, ifFalse)-1;  //agrega
848       Instrucs[n].aMatch:= atMatch;
849       Instrucs[n].aFail := atFail;
850       //Configura caracteres de contenido
851       for c := #0 to #255 do Instrucs[n].Chars[c] := False;
852       for c in list do Instrucs[n].Chars[c] := True;
853     end;
854   tregString: begin      //Es de tipo texto literal
855       n := AddItem(t, ifTrue, ifFalse)-1;  //agrega
856       Instrucs[n].aMatch:= atMatch;
857       Instrucs[n].aFail := atFail;
858       //configura cadena
859       if CaseSensitive then Instrucs[n].Text := str
860       else Instrucs[n].Text := UpCase(str);  //ignora caja
861     end;
862   tregTokPos: begin
863       n := AddItem(t, ifTrue, ifFalse)-1;  //agrega
864       Instrucs[n].aMatch:= atMatch;
865       Instrucs[n].aFail := atFail;
866       //configura cadena
867       Instrucs[n].tokPos:= StrToInt(str);  //Orden de token
868     end;
869   else
870     raise ESynFacilSyn.Create(ERR_UNSUPPOR_EXP_ + expr);
871   end;
872 end;
873 procedure tFaTokContent.AddInstruct(exp: string; ifTrue: string=''; ifFalse: string='';
874       atMatch: integer=-1; atFail: integer=-1);
875 //Agrega una instrucción para el procesamiento del token por contenido.
876 //Solo se debe indicar una instrucción, de otra forma se generará un error.
877 var
878   expr: String;
879 begin
880   expr := exp;   //guarda, porque se va a trozar
881   AddOneInstruct(exp, ifTrue, ifFalse, atMatch, atFail);  //si hay error genera excepción
882   //Si llegó aquí es porque se obtuvo una expresión válida, pero la
883   //expresión continua.
884   if exp<>'' then begin
885     raise ESynFacilSyn.Create(ERR_UNSUPPOR_EXP_ + expr);
886   end;
887 end;
888 procedure tFaTokContent.AddRegEx(exp: string; Complete: boolean = false);
889 {Agrega una expresión regular (un conjunto de instrucciones sin opciones de control), al
890 token por contenido. Las expresiones regulares deben ser solo las soportadas.
891 Ejemplos son:  "[0..9]*[\.][0..9]", "[A..Za..z]*"
892 Las expresiones se evalúan parte por parte. Si un token no coincide completamente con la
893 expresión regular, se considera al token, solamente hasta el punto en que coincide.
894 Si se produce algún error se generará una excepción.}
895 var
896   dToStart: Integer;
897 begin
898   if Complete then begin
899     //Cuando no coincide completamente, retrocede hasta el demimitador incial
900     dToStart := 0;  //distamcia al inicio
901     while exp<>'' do begin
902       AddOneInstruct(exp,'','exit(-'+ IntToStr(dToStart) + ')');
903       Inc(dToStart);
904     end;
905   end else begin
906     //La coinicidencia puede ser parcial
907     while exp<>'' do begin
908       AddOneInstruct(exp,'','');  //en principio, siempre debe coger una expresión
909     end;
910   end;
911 end;
912 
913 { TSynFacilSynBase }
GetSampleSourcenull914 function TSynFacilSynBase.GetSampleSource: String;
915 begin
916   Result := fSampleSource;
917 end;
918 //funciones básicas
TSynFacilSynBase.BuscTokEspecnull919 function TSynFacilSynBase.BuscTokEspec(var mat: TArrayTokSpec; cad: string;
920                          out n: integer; TokPos: integer = 0): boolean;
921 //Busca una cadena en una matriz TArrayTokSpec. Si la ubica devuelve el índice en "n".
922 var i : integer;
923 begin
924   Result := false;
925   if TokPos = 0 then begin //búsqueda normal
926     for i := 0 to High(mat) do begin
927       if mat[i].txt = cad then begin
928         n:= i;
929         exit(true);
930       end;
931     end;
932   end else begin  //búsqueda con TokPos
933     for i := 0 to High(mat) do begin
934       if (mat[i].txt = cad) and (TokPos = mat[i].TokPos) then begin
935         n:= i;
936         exit(true);
937       end;
938     end;
939   end;
940 end;
TSynFacilSynBase.ToListRegexnull941 function TSynFacilSynBase.ToListRegex(list: TFaXMLatrib): string;
942 //Reemplaza el contenido de una lista en foramto XML (p.ej. "A..Z") al formato de
943 //listas de expresiones regulares; "[A-Z]"
944 //Los caracteres "..", cambian a "-" y el caracter "-", cambia a "\-"
945 var
946   tmp: String;
947 begin
948   tmp := StringReplace(list.val, '-', '\-',[rfReplaceAll]);
949   tmp := StringReplace(tmp, '..', '-',[rfReplaceAll]);
950   Result := '[' + tmp + ']';  //completa con llaves
951 end;
TSynFacilSynBase.dStartRegexnull952 function TSynFacilSynBase.dStartRegex(tStart, tCharsStart: TFaXMLatrib): string;
953 //Lee los parámetros XML "Start" y "CharsStart"; y extrae el delimitador inicial
954 //a usar en formato de Expresión Regular.
955 begin
956   //validaciones
957   if tStart.hay and tCharsStart.hay then begin
958     //No es un caso válido que se den los dos parámetros
959     raise ESynFacilSyn.Create(ERR_NOT_USE_START);
960   end;
961   if not tStart.hay and not tCharsStart.hay then begin
962     //Tampoco es un caso válido que no se de ninguno.
963     raise ESynFacilSyn.Create(ERR_PAR_START_CHARS);
964   end;
965   //Hay uno u otro parámetro definido
966   if tStart.hay then begin
967     Result := EscapeText(tStart.val);  //protege a los caracteres especiales
968   end else if tCharsStart.hay then begin
969     Result := ToListRegex(tCharsStart);  //convierte a expresión regular como [a..z]
970   end;
971 end;
972 procedure TSynFacilSynBase.VerifDelim(delim: string);
973 //Verifica la validez de un delimitador para un token delimitado.
974 //Si hay error genera una excepción.
975 var c:char;
976     tmp: string;
977 begin
978   //verifica contenido
979   if delim = '' then
980     raise ESynFacilSyn.Create(ERR_TOK_DELIM_NULL);
981   //verifica si inicia con caracter de identificador.
982   if  delim[1] in charsIniIden then begin
983     //Empieza como identificador. Hay que verificar que todos los demás caracteres
984     //sean también de identificador, de otra forma no se podrá reconocer el token.
985     tmp := copy(delim, 2, length(delim) );
986     for c in tmp do
987       if not CharsIdentif[c] then begin
988         raise ESynFacilSyn.Create(format(ERR_TOK_DEL_IDE_ERR,[delim]));
989       end;
990   end;
991 end;
992 procedure TSynFacilSynBase.ValidateParamStart(Start: string; var ListElem: TStringList);
993 {Valida si la expresión del parámetro es de tipo <literal> o [<lista de cars>], de
994 otra forma generará una excepción.
995 Si es de tipo <literal>, valida que sea un delimitador válido.
996 Devuelve en "ListElem" una lista con con los caracteres (En el caso de [<lista de cars>])
997 o un solo elemento con una cadena (En el caso de <literal>). Por ejemplo:
998 Si Start = 'cadena', entonces se tendrá: ListElem = [ 'cadena' ]
999 Si Start = '[1..5]', entonces se tendrá: ListElem = ['0','1','2','3','4','5']
1000 Si encuentra error, genera excepción.}
1001 var
1002   t: tFaRegExpType;
1003   listChars: string;
1004   str: string;
1005   c: Char;
1006 begin
1007   if Start= '' then raise ESynFacilSyn.Create(ERR_START_NO_EMPTY);
1008   t := ExtractRegExp(Start, str, listChars);
1009   ListElem.Clear;
1010   if Start<>'' then  //la expresión es más compleja
1011     raise ESynFacilSyn.Create(ERR_EXP_MUST_BE_BR);
1012   if t = tregChars then begin
1013     for c in listChars do begin
1014       ListElem.Add(c);
1015     end;
1016   end else if t = tregString then begin  //lista simple o literal cadena
1017     VerifDelim(str);   //valida reglas
1018     lisTmp.Add(str);
1019   end else //expresión de otro tipo
1020     raise ESynFacilSyn.Create(ERR_EXP_MUST_BE_BR);
1021 end;
1022 procedure TSynFacilSynBase.ValidAsigDelim(delAct, delNue: TFaTypeDelim; delim: string);
1023 //Verifica si la asignación de delimitadores es válida. Si no lo es devuelve error.
1024 begin
1025   if delAct = tdNull then  exit;  //No estaba inicializado, es totalente factible
1026   //valida asignación de delimitador
1027   if (delAct in [tdUniLin, tdMulLin]) and
1028      (delNue in [tdUniLin, tdMulLin]) then begin
1029     raise ESynFacilSyn.Create(Format(ERR_IDEN_ALREA_DEL,[delim]));
1030   end;
1031 end;
KeyCompnull1032 function TSynFacilSynBase.KeyComp(var r: TTokSpec): Boolean; inline;
1033 {Compara rápidamente una cadena con el token actual, apuntado por "fToIden".
1034  El tamaño del token debe estar en "fStringLen"}
1035 var
1036   i: Integer;
1037   Temp: PChar;
1038 begin
1039   Temp := fToIdent;
1040   if Length(r.txt) = fStringLen then begin  //primera comparación
1041     if (r.TokPos <> 0) and (r.TokPos<>posTok) then exit(false);  //no coincide
1042     Result := True;  //valor por defecto
1043     for i := 1 to fStringLen do begin
1044       if TabMayusc[Temp^] <> r.txt[i] then exit(false);
1045       inc(Temp);
1046     end;
1047   end else  //definitívamente es diferente
1048     Result := False;
1049 end;
CreaBuscTokEspecnull1050 function TSynFacilSynBase.CreaBuscTokEspec(var mat: TArrayTokSpec; cad: string;
1051                                        out i:integer; TokPos: integer = 0): boolean;
1052 {Busca o crea el token especial indicado en "cad". Si ya existe, devuelve TRUE y
1053  actualiza "i" con su posición. Si no existe. Crea el token especial y devuelve la
1054  referencia en "i". Se le debe indicar la tabla a buscar en "mat"}
1055 var
1056   r:TTokSpec;
1057 begin
1058   if not CaseSensitive then cad:= UpCase(cad);  //cambia caja si es necesario
1059   if BuscTokEspec(mat, cad, i, TokPos) then exit(true);  //ya existe, devuelve en "i"
1060   //no existe, hay que crearlo. Aquí se definen las propiedades por defecto
1061   r.txt:=cad;         //se asigna el nombre
1062   r.TokPos:=TokPos;   //se asigna ordinal del token
1063   r.tTok:=-1;        //sin tipo asignado
1064   r.typDel:=tdNull;   //no es delimitador
1065   r.dEnd:='';         //sin delimitador final
1066   r.pRange:=nil;      //sin función de rango
1067   r.folTok:=false;    //sin plegado de token
1068   r.chrEsc := #0;       //sin caracter de escape
1069   r.openBlk:=false;    //sin plegado de bloque
1070   r.closeBlk:=false;    //sin plegado de bloque
1071   r.OpenSec:=false;    //no es sección de bloque
1072   r.firstSec:=nil;     //inicialmente no abre ningún bloque
1073 
1074   i := High(mat)+1;   //siguiente posición
1075   SetLength(mat,i+1); //hace espacio
1076   mat[i] := r;        //copia todo el registro
1077   //sale indicando que se ha creado
1078   Result := false;
1079 end;
1080 //procesamiento de XML
ReadXMLParamnull1081 function TSynFacilSynBase.ReadXMLParam(n: TDOMNode; nomb:string): TFaXMLatrib;
1082 //Explora un nodo para ver si existe un atributo, y leerlo. Ignora la caja.
1083 var
1084   i: integer;
1085   cad: string;
1086   atri: TDOMNode;
EsEnteronull1087   function EsEntero(txt: string; out num: integer): boolean;
1088   //convierte un texto en un número entero. Si es numérico devuelve TRUE
1089   var i: integer;
1090   begin
1091     Result := true;  //valor por defecto
1092     num := 0; //valor por defecto
1093     for i:=1 to length(txt) do begin
1094       if not (txt[i] in ['0'..'9']) then exit(false);  //no era
1095     end;
1096     //todos los dígitos son numéricos
1097     num := StrToInt(txt);
1098   end;
1099 begin
1100   Result.hay := false; //Se asume que no existe
1101   Result.val:='';      //si no encuentra devuelve vacío
1102   Result.bol:=false;   //si no encuentra devuelve Falso
1103   Result.n:=0;         //si no encuentra devuelve 0
1104   for i:= 0 to n.Attributes.Length-1 do begin
1105     atri := n.Attributes.Item[i];
1106     if UpCase(AnsiString(atri.NodeName)) = UpCase(nomb) then begin
1107       Result.hay := true;          //marca bandera
1108       Result.val := AnsiString(atri.NodeValue);  //lee valor
1109       Result.bol := UpCase(atri.NodeValue) = 'TRUE';  //lee valor booleano
1110       cad := trim(AnsiString(atri.NodeValue));  //valor sin espacios
1111       //lee número
1112       if (cad<>'') and (cad[1] in ['0'..'9']) then  //puede ser número
1113         EsEntero(cad,Result.n); //convierte
1114       //Lee color
1115       Result.col := ColorFromStr(cad);
1116     end;
1117   end;
1118 end;
1119 procedure TSynFacilSynBase.CheckXMLParams(n: TDOMNode; listAtrib: string);
1120 //Valida la existencia completa de los nodos indicados. Si encuentra alguno más
1121 //genera excepción. Los nodos deben estar separados por espacios.
1122 var i,j   : integer;
1123     atri  : TDOMNode;
1124     nombre, tmp : string;
1125     hay   : boolean;
1126 begin
1127   //Carga lista de atributos
1128   lisTmp.Clear;  //usa lista temproal
1129   lisTmp.Delimiter := ' ';
1130   //StringReplace(listSym, #13#10, ' ',[rfReplaceAll]);
1131   lisTmp.DelimitedText := listAtrib;
1132   //Realiza la verificación
1133   for i:= 0 to n.Attributes.Length-1 do begin
1134     atri := n.Attributes.Item[i];
1135     nombre := UpCase(AnsiString(atri.NodeName));
1136     //verifica existencia
1137     hay := false;
1138     for j:= 0 to lisTmp.Count -1 do begin
1139       tmp := trim(lisTmp[j]);
1140       if nombre = UpCase(tmp) then begin
1141          hay := true; break;
1142       end;
1143     end;
1144     //verifica si no existe
1145     if not hay then begin   //Este atributo está demás
1146       raise ESynFacilSyn.Create(format(ERR_INVAL_ATTR_LAB,[atri.NodeName, n.NodeName]));
1147     end;
1148   end;
1149 end;
1150 ////Métodos para tokens por contenido
1151 procedure TSynFacilSynBase.metTokCont(const tc: tFaTokContent); //inline;
1152 //Procesa tokens por contenido
1153 var
1154   n,i : Integer;
1155   posFin0: Integer;
1156   nf  : Integer;
1157   tam1: Integer;
1158   inst: tFaTokContentInstPtr;
1159 begin
1160   fTokenID := tc.TokTyp;   //No debería ser necesario ya que se asignará después.
1161   inc(posFin);  //para pasar al siguiente caracter
1162   n := 0;
1163   while n<tc.nInstruc do begin
1164     inst := @tc.Instrucs[n];  //Para acceso rápido
1165     inst^.posFin := posFin;  //guarda posición al iniciar
1166     case inst^.expTyp of
1167     tregTokPos: begin   //TokPos=?
1168         //Se verifica posición de token
1169       //verifica la coincidencia
1170       if inst^.tokPos = posTok then begin //cumple
1171         if inst^.aMatch<>-1 then fTokenID := inst^.aMatch;  //pone atributo
1172         case inst^.actionMatch of
1173         aomNext:;   //no hace nada, pasa al siguiente elemento
1174         aomExit: break;    //simplemente sale
1175         aomExitpar: begin  //sale con parámetro
1176           nf := inst^.destOnMatch;   //lee posición final
1177           posFin := tc.Instrucs[nf].posFin;  //Debe moverse antes de salir
1178           break;
1179         end;
1180         aomMovePar: begin  //se mueve a una posición
1181           n := inst^.destOnMatch;   //ubica posición
1182           continue;
1183         end;
1184         end;
1185       end else begin      //no cumple
1186         if inst^.aFail<>-1 then fTokenID := inst^.aFail;  //pone atributo
1187         case inst^.actionFail of
1188         aomNext:;   //no hace nada, pasa al siguiente elemento
1189         aomExit: break;    //simplemente sale
1190         aomExitpar: begin  //sale con parámetro
1191           nf := inst^.destOnFail;   //lee posición final
1192           posFin := tc.Instrucs[nf].posFin;  //Debe moverse antes de salir
1193           break;
1194         end;
1195         aomMovePar: begin  //se mueve a una posición
1196           n := inst^.destOnFail;   //ubica posición
1197           continue;
1198         end;
1199         end;
1200       end;
1201     end;
1202 
1203     tregString: begin  //texo literal
1204         //Rutina de comparación de cadenas
1205         posFin0 := posFin;  //para poder restaurar
1206         i := 1;
1207         tam1 := length(inst^.Text)+1;  //tamaño +1
1208         if CaseSensitive then begin  //sensible a caja
1209           while (i<tam1) and (inst^.Text[i] = fLine[posFin]) do begin
1210             inc(posFin);
1211             inc(i);
1212           end;
1213         end else begin  //Ignora mayúcula/minúscula
1214           while (i<tam1) and (inst^.Text[i] = TabMayusc[fLine[posFin]]) do begin
1215             inc(posFin);
1216             inc(i);
1217           end;
1218         end;
1219         //verifica la coincidencia
1220         if i = tam1 then begin //cumple
1221           if inst^.aMatch<>-1 then fTokenID := inst^.aMatch;  //pone atributo
1222           case inst^.actionMatch of
1223           aomNext:;   //no hace nada, pasa al siguiente elemento
1224           aomExit: break;    //simplemente sale
1225           aomExitpar: begin  //sale con parámetro
1226             nf := inst^.destOnMatch;   //lee posición final
1227             posFin := tc.Instrucs[nf].posFin;  //Debe moverse antes de salir
1228             break;
1229           end;
1230           aomMovePar: begin  //se mueve a una posición
1231             n := inst^.destOnMatch;   //ubica posición
1232             continue;
1233           end;
1234           end;
1235         end else begin      //no cumple
1236           if inst^.aFail<>-1 then fTokenID := inst^.aFail;  //pone atributo
1237           posFin := posFin0;   //restaura posición
1238           case inst^.actionFail of
1239           aomNext:;   //no hace nada, pasa al siguiente elemento
1240           aomExit: break;    //simplemente sale
1241           aomExitpar: begin  //sale con parámetro
1242             nf := inst^.destOnFail;   //lee posición final
1243             posFin := tc.Instrucs[nf].posFin;  //Debe moverse antes de salir
1244             break;
1245           end;
1246           aomMovePar: begin  //se mueve a una posición
1247             n := inst^.destOnFail;   //ubica posición
1248             continue;
1249           end;
1250           end;
1251         end;
1252       end;
1253     tregChars: begin   //conjunto de caracteres: [ ... ]
1254         //debe existir solo una vez
1255         if inst^.Chars[fLine[posFin]] then begin
1256           //cumple el caracter
1257           if inst^.aMatch<>-1 then fTokenID := inst^.aMatch;  //pone atributo
1258           inc(posFin);  //pasa a la siguiente instrucción
1259           //Cumple el caracter
1260           case inst^.actionMatch of
1261           aomNext:;   //no hace nada, pasa al siguiente elemento
1262           aomExit: break;    //simplemente sale
1263           aomExitpar: begin  //sale con parámetro
1264             nf := inst^.destOnMatch;   //lee posición final
1265             posFin := tc.Instrucs[nf].posFin;  //Debe moverse antes de salir
1266             break;
1267           end;
1268           aomMovePar: begin  //se mueve a una posición
1269             n := inst^.destOnMatch;   //ubica posición
1270             continue;
1271           end;
1272           end;
1273         end else begin
1274           //no se encuentra ningún caracter de la lista
1275           if inst^.aFail<>-1 then fTokenID := inst^.aFail;  //pone atributo
1276           case inst^.actionFail of
1277           aomNext:;   //no hace nada, pasa al siguiente elemento
1278           aomExit: break;    //simplemente sale
1279           aomExitpar: begin  //sale con parámetro
1280             nf := inst^.destOnFail;   //lee posición final
1281             posFin := tc.Instrucs[nf].posFin;  //Debe moverse antes de salir
1282             break;
1283           end;
1284           aomMovePar: begin  //se mueve a una posición
1285             n := inst^.destOnFail;   //ubica posición
1286             continue;
1287           end;
1288           end;
1289         end;
1290     end;
1291     tregChars01: begin   //conjunto de caracteres: [ ... ]?
1292         //debe existir cero o una vez
1293         if inst^.Chars[fLine[posFin]] then begin
1294           inc(posFin);  //pasa a la siguiente instrucción
1295         end;
1296         //siempre cumplirá este tipo, no hay nada que verificar
1297         if inst^.aMatch<>-1 then fTokenID := inst^.aMatch;  //pone atributo
1298         case inst^.actionMatch of
1299         aomNext:;   //no hace nada, pasa al siguiente elemento
1300         aomExit: break;    //simplemente sale
1301         aomExitpar: begin  //sale con parámetro
1302           nf := inst^.destOnMatch;   //lee posición final
1303           posFin := tc.Instrucs[nf].posFin;  //Debe moverse antes de salir
1304           break;
1305         end;
1306         aomMovePar: begin  //se mueve a una posición
1307           n := inst^.destOnMatch;   //ubica posición
1308           continue;
1309         end;
1310         end;
1311     end;
1312     tregChars0_: begin   //conjunto de caracteres: [ ... ]*
1313         //debe exitir 0 o más veces
1314         while inst^.Chars[fLine[posFin]] do begin
1315           inc(posFin);
1316         end;
1317         //siempre cumplirá este tipo, no hay nada que verificar
1318         if inst^.aMatch<>-1 then fTokenID := inst^.aMatch;  //pone atributo
1319         //¿No debería haber código aquí también?
1320       end;
1321     tregChars1_: begin   //conjunto de caracteres: [ ... ]+
1322         //debe existir una o más veces
1323         posFin0 := posFin;  //para poder comparar
1324         while inst^.Chars[fLine[posFin]] do begin
1325           inc(posFin);
1326         end;
1327         if posFin>posFin0 then begin   //Cumple el caracter
1328           if inst^.aMatch<>-1 then fTokenID := inst^.aMatch;  //pone atributo
1329           case inst^.actionMatch of
1330           aomNext:;   //no hace nada, pasa al siguiente elemento
1331           aomExit: break;    //simplemente sale
1332           aomExitpar: begin  //sale con parámetro
1333             nf := inst^.destOnMatch;   //lee posición final
1334             posFin := tc.Instrucs[nf].posFin;  //Debe moverse antes de salir
1335             break;
1336           end;
1337           aomMovePar: begin  //se mueve a una posición
1338             n := inst^.destOnMatch;   //ubica posición
1339             continue;
1340           end;
1341           end;
1342         end else begin   //No cumple
1343           if inst^.aFail<>-1 then fTokenID := inst^.aFail;  //pone atributo
1344           case inst^.actionFail of
1345           aomNext:;   //no hace nada, pasa al siguiente elemento
1346           aomExit: break;    //simplemente sale
1347           aomExitpar: begin  //sale con parámetro
1348             nf := inst^.destOnFail;   //lee posición final
1349             posFin := tc.Instrucs[nf].posFin;  //Debe moverse antes de salir
1350             break;
1351           end;
1352           aomMovePar: begin  //se mueve a una posición
1353             n := inst^.destOnFail;   //ubica posición
1354             continue;
1355           end;
1356           end;
1357         end;
1358       end;
1359     end;
1360     inc(n);
1361   end;
1362 end;
1363 procedure TSynFacilSynBase.metTokCont1; //Procesa tokens por contenido 1
1364 begin
1365   metTokCont(tc1);
1366 end;
1367 procedure TSynFacilSynBase.metTokCont2; //Procesa tokens por contenido 2
1368 begin
1369   metTokCont(tc2);
1370 end;
1371 procedure TSynFacilSynBase.metTokCont3; //Procesa tokens por contenido 3
1372 begin
1373   metTokCont(tc3);
1374 end;
1375 procedure TSynFacilSynBase.metTokCont4; //Procesa tokens por contenido 3
1376 begin
1377   metTokCont(tc4);
1378 end;
1379 //Procesamiento de otros elementos
1380 procedure TSynFacilSynBase.metIdent;
1381 //Procesa el identificador actual
1382 begin
1383   inc(posFin);  {debe incrementarse, para pasar a comparar los caracteres siguientes,
1384                  o de otra forma puede quedarse en un lazo infinito}
1385   while CharsIdentif[fLine[posFin]] do inc(posFin);
1386   fTokenID := tnIdentif;  //identificador común
1387 end;
1388 procedure TSynFacilSynBase.metIdentUTF8;
1389 //Procesa el identificador actual. considerando que empieza con un caracter UTF8 (dos bytes)
1390 begin
1391   inc(posFin);  {es UTF8, solo filtra por el primer caracter (se asume que el segundo
1392                  es siempre válido}
1393   inc(posFin);  {debe incrementarse, para pasar a comparar los caracteres siguientes,
1394                  o de otra forma puede quedarse en un lazo infinito}
1395   while CharsIdentif[fLine[posFin]] do inc(posFin);
1396   fTokenID := tnIdentif;  //identificador común
1397 end;
1398 procedure TSynFacilSynBase.metNull;
1399 //Procesa la ocurrencia del cacracter #0
1400 begin
1401   fTokenID := tnEol;   //Solo necesita esto para indicar que se llegó al final de la línae
1402 end;
1403 procedure TSynFacilSynBase.metSpace;
1404 //Procesa caracter que es inicio de espacio
1405 begin
1406   fTokenID := tnSpace;
1407   repeat  //captura todos los que sean espacios
1408     Inc(posFin);
1409   until (fLine[posFin] > #32) or (posFin = tamLin);
1410 end;
1411 procedure TSynFacilSynBase.metSymbol;
1412 begin
1413   inc(posFin);
1414   while (fProcTable[fLine[posFin]] = @metSymbol)
1415   do inc(posFin);
1416   fTokenID := tnSymbol;
1417 end;
1418 //Funciones públicas
1419 procedure TSynFacilSynBase.DefTokIdentif(dStart, Content: string );
1420 {Define token para identificadores. Los parámetros deben ser intervalos.
1421 El parámetro "dStart" deben ser de la forma: "[A..Za..z]"
1422 El parámetro "charsCont" deben ser de la forma: "[A..Za..z]*"
1423 Si los parámetros no cumplen con el formato se generará una excepción.
1424 Se debe haber limpiado previamente con "ClearMethodTables"}
1425 var
1426   c : char;
1427   t : tFaRegExpType;
1428   listChars: string;
1429   str: string;
1430 begin
1431   /////// Configura caracteres de inicio
1432   if dStart = '' then exit;   //protección
1433   t := ExtractRegExp(dStart, str, listChars);
1434   if (t <> tregChars) or (dStart<>'') then  //solo se permite el formato [ ... ]
1435     raise ESynFacilSyn.Create(ERR_BAD_PAR_STR_IDEN);
1436   //Agrega evento manejador en caracteres iniciales
1437   charsIniIden := [];  //inicia
1438   for c in listChars do begin //permite cualquier caracter inicial
1439     if c<#128 then begin  //caracter normal
1440       fProcTable[c] := @metIdent;
1441       charsIniIden += [c];  //agrega
1442     end else begin   //caracter UTF-8
1443       fProcTable[c] := @metIdentUTF8;
1444       charsIniIden += [c];  //agrega
1445     end;
1446   end;
1447   /////// Configura caracteres de contenido
1448   t := ExtractRegExp(Content, str, listChars);
1449   if (t <> tregChars0_) or (Content<>'') then  //solo se permite el formato [ ... ]*
1450     raise ESynFacilSyn.Create(ERR_BAD_PAR_CON_IDEN);
1451   //limpia matriz
1452   for c := #0 to #255 do begin
1453     CharsIdentif[c] := False;
1454     //aprovecha para crear la tabla de mayúsculas para comparaciones
1455     if CaseSensitive then
1456       TabMayusc[c] := c
1457     else begin  //pasamos todo a mayúscula
1458       TabMayusc[c] := UpCase(c);
1459     end;
1460   end;
1461   //marca las posiciones apropiadas
1462   for c in listChars do CharsIdentif[c] := True;
1463 end;
1464 //Manejo de atributos
TSynFacilSynBase.NewTokAttribnull1465 function TSynFacilSynBase.NewTokAttrib(TypeName: string; out TokID: integer
1466   ): TSynHighlighterAttributes;
1467 {Crea un nuevo atributo y lo agrega al resaltador. Este debe ser el único punto de
1468 entrada, para crear atributos en SynFacilSyn. En tokID, se devuelve el ID del nuevo tipo.
1469 No hay funciones para eliminar atributs creados.}
1470 var
1471   n: Integer;
1472 begin
1473   Result := TSynHighlighterAttributes.Create(TypeName);
1474   n := High(Attrib)+1;   //tamaño
1475   setlength(Attrib, n + 1);  //incrementa tamaño
1476   Attrib[n] := Result;  //guarda la referencia
1477   tokID := n;           //devuelve ID
1478   AddAttribute(Result);   //lo registra en el resaltador
1479 end;
NewTokTypenull1480 function TSynFacilSynBase.NewTokType(TypeName: string; out
1481   TokAttrib: TSynHighlighterAttributes): integer;
1482 {Crea un nuevo tipo de token, y devuelve la referencia al atributo en "TokAttrib".}
1483 begin
1484   TokAttrib := NewTokAttrib(TypeName, Result);
1485 end;
1486 
NewTokTypenull1487 function TSynFacilSynBase.NewTokType(TypeName: string): integer;
1488 {Versión simplificada de NewTokType, que devuelve directamente el ID del token}
1489 begin
1490   NewTokAttrib(TypeName, Result);
1491 end;
1492 
1493 procedure TSynFacilSynBase.CreateAttributes;
1494 //CRea los atributos por defecto
1495 begin
1496   //Elimina todos los atributos creados, los fijos y los del usuario.
1497   FreeHighlighterAttributes;
1498   setlength(Attrib, 0);  //limpia
1499   { Crea los atributos que siempre existirán. }
1500   tkEol     := NewTokAttrib('Eol', tnEol);      //atributo de nulos
1501   tkSymbol  := NewTokAttrib('Symbol', tnSymbol);   //atributo de símbolos
1502   tkSpace   := NewTokAttrib('Space', tnSpace);    //atributo de espacios.
1503   tkIdentif := NewTokAttrib('Identifier', tnIdentif); //Atributo para identificadores.
1504   tkNumber  := NewTokAttrib('Number', tnNumber);   //atributo de números
1505   tkNumber.Foreground := clFuchsia;
1506   tkKeyword := NewTokAttrib('Keyword',tnKeyword);      //atribuuto de palabras claves
1507   tkKeyword.Foreground:=clGreen;
1508   tkString  := NewTokAttrib('String', tnString);   //atributo de cadenas
1509   tkString.Foreground := clBlue;
1510   tkComment := NewTokAttrib('Comment', tnComment);  //atributo de comentarios
1511   tkComment.Style := [fsItalic];
1512   tkComment.Foreground := clGray;
1513 end;
GetAttribByNamenull1514 function TSynFacilSynBase.GetAttribByName(txt: string): TSynHighlighterAttributes;
1515 {Devuelve la referencia de un atributo, recibiendo su nombre. Si no lo encuentra
1516 devuelve NIL.}
1517 var
1518   i: Integer;
1519 begin
1520   txt := UpCase(txt);   //ignora la caja
1521   //También lo puede buscar en Attrib[]
1522   for i:=0 to AttrCount-1 do begin
1523     if Upcase(Attribute[i].Name) = txt then begin
1524         Result := Attribute[i];  //devuelve índice
1525         exit;
1526     end;
1527   end;
1528   //No se encontró
1529   exit(nil);
1530 end;
TSynFacilSynBase.GetAttribIDByNamenull1531 function TSynFacilSynBase.GetAttribIDByName(txt: string): integer;
1532 {Devuelve el identificador de un atributo, recibiendo su nombre. Si no lo encuentra
1533 devuelve -1.}
1534 var
1535   i: Integer;
1536 begin
1537   txt := UpCase(txt);   //ignora la caja
1538   //Se tiene que buscar en Attrib[], proque allí están con los índices cprrectos
1539   for i:=0 to AttrCount-1 do begin
1540     if Upcase(Attrib[i].Name) = txt then begin
1541         Result := i;  //devuelve índice
1542         exit;
1543     end;
1544   end;
1545   //No se encontró
1546   exit(-1);
1547 end;
1548 
TSynFacilSynBase.IsAttributeNamenull1549 function TSynFacilSynBase.IsAttributeName(txt: string): boolean;
1550 //Verifica si una cadena corresponde al nombre de un atributo.
1551 begin
1552   //primera comparación
1553   if GetAttribByName(txt) <> nil then exit(true);
1554   //puede que haya sido "NULL"
1555   if UpCase(txt) = 'NULL' then exit(true);
1556   //definitivamente no es
1557   Result := False;
1558 end;
ProcXMLattributenull1559 function TSynFacilSynBase.ProcXMLattribute(nodo: TDOMNode): boolean;
1560 //Verifica si el nodo tiene la etiqueta <ATTRIBUTTE>. De ser así, devuelve TRUE y lo
1561 //procesa. Si encuentra error, genera una excepción.
1562 var
1563   tName: TFaXMLatrib;
1564   tBackCol: TFaXMLatrib;
1565   tForeCol: TFaXMLatrib;
1566   tFrameCol: TFaXMLatrib;
1567   tFrameEdg: TFaXMLatrib;
1568   tFrameSty: TFaXMLatrib;
1569   tStyBold: TFaXMLatrib;
1570   tStyItal: TFaXMLatrib;
1571   tStyUnder: TFaXMLatrib;
1572   tStyStrike: TFaXMLatrib;
1573   tStyle: TFaXMLatrib;
1574   tipTok: TSynHighlighterAttributes;
1575   Atrib: TSynHighlighterAttributes;
1576   tokId: integer;
1577 begin
1578   if UpCase(nodo.NodeName) <> 'ATTRIBUTE' then exit(false);
1579   Result := true;  //encontró
1580   ////////// Lee parámetros //////////
1581   tName    := ReadXMLParam(nodo,'Name');
1582   tBackCol := ReadXMLParam(nodo,'BackCol');
1583   tForeCol := ReadXMLParam(nodo,'ForeCol');
1584   tFrameCol:= ReadXMLParam(nodo,'FrameCol');
1585   tFrameEdg:= ReadXMLParam(nodo,'FrameEdg');
1586   tFrameSty:= ReadXMLParam(nodo,'FrameSty');
1587   tStyBold := ReadXMLParam(nodo,'Bold');
1588   tStyItal := ReadXMLParam(nodo,'Italic');
1589   tStyUnder:= ReadXMLParam(nodo,'Underline');
1590   tStyStrike:=ReadXMLParam(nodo,'StrikeOut');
1591   tStyle   := ReadXMLParam(nodo,'Style');
1592   CheckXMLParams(nodo, 'Name BackCol ForeCol FrameCol FrameEdg FrameSty '+
1593                          'Bold Italic Underline StrikeOut Style');
1594   ////////// cambia atributo //////////
1595   if IsAttributeName(tName.val)  then begin
1596     tipTok := GetAttribByName(tName.val);   //tipo de atributo
1597   end else begin
1598     //No existe, se crea.
1599     tipTok := NewTokAttrib(tName.val, tokId);
1600   end;
1601   //obtiene referencia
1602   Atrib := tipTok;
1603   //asigna la configuración del atributo
1604   if Atrib <> nil then begin
1605      if tBackCol.hay then Atrib.Background:=tBackCol.col;
1606      if tForeCol.hay then Atrib.Foreground:=tForeCol.col;
1607      if tFrameCol.hay then Atrib.FrameColor:=tFrameCol.col;
1608      if tFrameEdg.hay then begin
1609        case UpCase(tFrameEdg.val) of
1610        'AROUND':Atrib.FrameEdges:=sfeAround;
1611        'BOTTOM':Atrib.FrameEdges:=sfeBottom;
1612        'LEFT':  Atrib.FrameEdges:=sfeLeft;
1613        'NONE':  Atrib.FrameEdges:=sfeNone;
1614        end;
1615      end;
1616      if tFrameSty.hay then begin
1617        case UpCase(tFrameSty.val) of
1618        'SOLID': Atrib.FrameStyle:=slsSolid;
1619        'DASHED':Atrib.FrameStyle:=slsDashed;
1620        'DOTTED':Atrib.FrameStyle:=slsDotted;
1621        'WAVED': Atrib.FrameStyle:=slsWaved;
1622        end;
1623      end;
1624      if tStyBold.hay then begin  //negrita
1625         if tStyBold.bol then Atrib.Style:=Atrib.Style+[fsBold]
1626         else Atrib.Style:=Atrib.Style-[fsBold];
1627      end;
1628      if tStyItal.hay then begin  //cursiva
1629         if tStyItal.bol then Atrib.Style:=Atrib.Style+[fsItalic]
1630         else Atrib.Style:=Atrib.Style-[fsItalic];
1631      end;
1632      if tStyUnder.hay then begin  //subrayado
1633         if tStyUnder.bol then Atrib.Style:=Atrib.Style+[fsUnderline]
1634         else Atrib.Style:=Atrib.Style-[fsUnderline];
1635      end;
1636      if tStyStrike.hay then begin //tachado
1637         if tStyStrike.bol then Atrib.Style:=Atrib.Style+[fsStrikeOut]
1638         else Atrib.Style:=Atrib.Style-[fsStrikeOut];
1639      end;
1640      if tStyle.hay then begin  //forma alternativa
1641        Atrib.Style:=Atrib.Style-[fsBold]-[fsItalic]-[fsUnderline]-[fsStrikeOut];
1642        if Pos('b', tStyle.val)<>0 then Atrib.Style:=Atrib.Style+[fsBold];
1643        if Pos('i', tStyle.val)<>0 then Atrib.Style:=Atrib.Style+[fsItalic];
1644        if Pos('u', tStyle.val)<>0 then Atrib.Style:=Atrib.Style+[fsUnderline];
1645        if Pos('s', tStyle.val)<>0 then Atrib.Style:=Atrib.Style+[fsStrikeOut];
1646      end;
1647   end;
1648 end;
1649 constructor TSynFacilSynBase.Create(AOwner: TComponent);
1650 begin
1651   inherited Create(AOwner);
1652   setlength(Attrib, 0);
1653 end;
1654 
1655 var
1656   i: integer;
1657 initialization
1658   //prepara definición de comodines
1659   bajos[0] := #127;
1660   for i:=1 to 127 do bajos[i] := chr(i);  //todo menos #0
1661   altos[0] := #128;
1662   for i:=1 to 128 do altos[i] := chr(i+127);
1663 
1664 end.
1665 
1666