cldrtxt.pas 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  1. { Parser of the CLDR collation tailoring files.
  2. This parser handle the textual syntax for CLDR version > 23
  3. Copyright (c) 2014,2015 by Inoussa OUEDRAOGO
  4. The source code is distributed under the Library GNU
  5. General Public License with the following modification:
  6. - object files and libraries linked into an application may be
  7. distributed without source code.
  8. If you didn't receive a copy of the file COPYING, contact:
  9. Free Software Foundation
  10. 675 Mass Ave
  11. Cambridge, MA 02139
  12. USA
  13. This program is distributed in the hope that it will be useful,
  14. but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  16. }
  17. unit cldrtxt;
  18. {$mode objfpc}{$H+}
  19. {$TypedAddress on}
  20. interface
  21. uses
  22. Classes, SysUtils,
  23. cldrhelper, helper;
  24. procedure ParseInitialDocument(ASequence : POrderedCharacters; ADoc : TCustomMemoryStream);overload;
  25. procedure ParseInitialDocument(ASequence : POrderedCharacters; AFileName : string);overload;
  26. function ParseStatement(
  27. AData : PAnsiChar;
  28. AStartPosition,
  29. AMaxLen : Integer;
  30. AStatement : PReorderSequence;
  31. var ANextPos,
  32. ALineCount : Integer
  33. ) : Boolean;
  34. implementation
  35. uses
  36. unicodedata;
  37. const
  38. s_BEFORE = 'before';
  39. function String2UnicodeCodePointArray(const AValue : UTF8String): TUnicodeCodePointArray;
  40. var
  41. u4str : UCS4String;
  42. k : Integer;
  43. begin
  44. if (Length(AValue) = 0) then
  45. exit(nil);
  46. if (Length(AValue) = 1) then begin
  47. SetLength(Result,1);
  48. Result[0] := Ord(AValue[1])
  49. end else begin
  50. u4str := UnicodeStringToUCS4String(UTF8Decode(AValue));
  51. k := Length(u4str) - 1; // remove the last #0
  52. SetLength(Result,k);
  53. for k := 0 to k - 1 do
  54. Result[k] := u4str[k];
  55. end;
  56. end;
  57. function TryStringToReorderWeigthKind(
  58. const AStr : UTF8String;
  59. out AResult : TReorderWeigthKind
  60. ) : Boolean;
  61. begin
  62. Result := True;
  63. if (AStr = '=') then
  64. AResult := TReorderWeigthKind.Identity
  65. else if (AStr = '<') or (AStr = '>') then
  66. AResult := TReorderWeigthKind.Primary
  67. else if (AStr = '<<') or (AStr = '>>') then
  68. AResult := TReorderWeigthKind.Secondary
  69. else if (AStr = '<<<') or (AStr = '>>>') then
  70. AResult := TReorderWeigthKind.Tertiary
  71. else begin
  72. AResult := TReorderWeigthKind.Identity;
  73. Result := False;
  74. end;
  75. end;
  76. function ParseStatement(
  77. AData : PAnsiChar;
  78. AStartPosition,
  79. AMaxLen : Integer;
  80. AStatement : PReorderSequence;
  81. var ANextPos,
  82. ALineCount : Integer
  83. ) : Boolean;
  84. const
  85. LINE_LENGTH = 1024;
  86. var
  87. p : PAnsiChar;
  88. bufferLength, bufferPos, lineLength, linePos, lineIndex : Integer;
  89. line : UTF8String;
  90. statement : PReorderSequence;
  91. elementActualCount : Integer;
  92. specialChararter : Boolean;
  93. historyItemIndex : Integer;
  94. historyItems : array[0..31] of record
  95. p : PAnsiChar;
  96. bufferLength,
  97. bufferPos,
  98. lineLength,
  99. linePos,
  100. lineIndex : Integer;
  101. line : UTF8String;
  102. end;
  103. procedure SaveState();
  104. begin
  105. if (historyItemIndex >= High(historyItems)) then
  106. raise Exception.Create('History buffer is full.');
  107. historyItemIndex := historyItemIndex+1;
  108. historyItems[historyItemIndex].p := p;
  109. historyItems[historyItemIndex].bufferLength := bufferLength;
  110. historyItems[historyItemIndex].bufferPos := bufferPos;
  111. historyItems[historyItemIndex].lineLength := lineLength;
  112. historyItems[historyItemIndex].linePos := linePos;
  113. historyItems[historyItemIndex].lineIndex := lineIndex;
  114. historyItems[historyItemIndex].line := line;
  115. end;
  116. procedure RestoreState();
  117. begin
  118. if (historyItemIndex < 0) then
  119. raise Exception.Create('History buffer is empty.');
  120. p := historyItems[historyItemIndex].p;
  121. bufferLength := historyItems[historyItemIndex].bufferLength;
  122. bufferPos := historyItems[historyItemIndex].bufferPos;
  123. lineLength := historyItems[historyItemIndex].lineLength;
  124. linePos := historyItems[historyItemIndex].linePos;
  125. lineIndex := historyItems[historyItemIndex].lineIndex;
  126. line := historyItems[historyItemIndex].line;
  127. historyItemIndex := historyItemIndex-1;
  128. end;
  129. procedure DiscardState();
  130. begin
  131. if (historyItemIndex < 0) then
  132. raise Exception.Create('History buffer is empty.');
  133. historyItemIndex := historyItemIndex-1;
  134. end;
  135. function CurrentLine() : UTF8String; inline;
  136. begin
  137. Result := Copy(line,1,lineLength);
  138. end;
  139. function NextLine() : Boolean;
  140. var
  141. locOldPos : Integer;
  142. locOldPointer : PAnsiChar;
  143. begin
  144. Result := False;
  145. if (p^ = #10) then begin
  146. Inc(p);
  147. Inc(bufferPos);
  148. end;
  149. locOldPos := bufferPos;
  150. locOldPointer := p;
  151. while (bufferPos < bufferLength) and (p^ <> #10) do begin
  152. Inc(p);
  153. Inc(bufferPos);
  154. end;
  155. if (locOldPos = bufferPos) and (p^ = #10) then begin
  156. lineLength := 0;
  157. Inc(p);
  158. Inc(bufferPos);
  159. linePos := 1;
  160. Result := True;
  161. end else if (locOldPos < bufferPos) then begin
  162. lineLength := (bufferPos - locOldPos);
  163. if (lineLength >= Length(line)) then
  164. SetLength(line,(2*lineLength));
  165. Move(locOldPointer^,line[1],lineLength);
  166. {if (p^ = #10) then begin
  167. //Dec(lineLength);
  168. Inc(p);
  169. Inc(bufferPos);
  170. end;}
  171. linePos := 1;
  172. Result := True;
  173. end;
  174. if Result and (locOldPos < bufferPos) then
  175. lineIndex := lineIndex+1;
  176. end;
  177. procedure CheckLineLength(const ALength : Integer);
  178. begin
  179. if (ALength > lineLength) then
  180. raise Exception.CreateFmt('Unexpected end of line : "%s".',[CurrentLine()]);
  181. end;
  182. function ReadChar(out AResult : UTF8String) : Boolean;
  183. var
  184. k : Integer;
  185. us : UnicodeString;
  186. begin
  187. AResult := '';
  188. Result := False;
  189. if (linePos > lineLength) then
  190. exit;
  191. {if CharInSet(line[linePos],['#','=','&','[',']']) then begin
  192. AResult := line[linePos];
  193. Inc(linePos);
  194. exit(True);
  195. end;}
  196. if (line[linePos] <> '\') then begin
  197. AResult := line[linePos];
  198. Inc(linePos);
  199. exit(True);
  200. end;
  201. CheckLineLength(linePos+1);
  202. Inc(linePos);
  203. case line[linePos] of
  204. '''': begin
  205. AResult := '\';
  206. exit(True);
  207. end;
  208. {'\' : begin
  209. AResult := '\';
  210. exit(True);
  211. end;}
  212. 'u' : begin
  213. CheckLineLength(linePos+4);
  214. AResult := '$'+Copy(line,(linePos+1),4);
  215. if not TryStrToInt(AResult,k) then
  216. raise Exception.CreateFmt('Hexadecimal Integer expected but found "%s", line = "%s".',[AResult,CurrentLine()]);
  217. SetLength(us,1);
  218. us[1] := UnicodeChar(k);
  219. AResult := UTF8Encode(us);
  220. Inc(linePos,5);
  221. exit(True);
  222. end;
  223. 'U' : begin
  224. CheckLineLength(linePos+8);
  225. AResult := '$'+Copy(line,(linePos+1),8);
  226. if not TryStrToInt(AResult,k) then
  227. raise Exception.CreateFmt('Hexadecimal Integer expected but found "%s".',[AResult]);
  228. if (k > High(Word)) then begin
  229. SetLength(us,2);
  230. FromUCS4(k,us[1],us[2]);
  231. if (Ord(us[2]) = 0) then
  232. SetLength(us,1);
  233. end else begin
  234. SetLength(us,1);
  235. us[1] := UnicodeChar(k);
  236. end;
  237. AResult := UTF8Encode(us);
  238. Inc(linePos,9);
  239. exit(True);
  240. end;
  241. else
  242. raise Exception.CreateFmt('Invalide escaped string "%s", at %d position.',[CurrentLine(),linePos]);
  243. end;
  244. end;
  245. function ReadQuotedString() : UTF8String;
  246. var
  247. ks : UTF8String;
  248. begin
  249. if (line[linePos] <> '''') then
  250. raise Exception.CreateFmt('Unexpected character found "%s", a quote expected: "%s".',[line[linePos],CurrentLine()]);
  251. Inc(linePos);
  252. if (linePos > lineLength) then
  253. raise Exception.CreateFmt('Unexpected end of line, a quote expected: "%s".',[CurrentLine()]);
  254. if (line[linePos] = '''') then begin
  255. Inc(linePos);
  256. Result := '''';
  257. exit;
  258. end;
  259. Result := '';
  260. while (linePos <= lineLength) and ReadChar(ks) do begin
  261. Result := Result + ks;
  262. if (line[linePos] = '''') then
  263. break;
  264. end;
  265. if (line[linePos] = '''') then begin
  266. Inc(linePos);
  267. exit;
  268. end;
  269. raise Exception.CreateFmt('Unexpected end of line, a quote expected: "%s".',[line]);
  270. end;
  271. function ReadUnQuotedString() : UTF8String;
  272. var
  273. k : Integer;
  274. begin
  275. k := linePos;
  276. while (linePos <= lineLength) and
  277. not(CharInSet(line[linePos],[' ',#9,'#', '=','&','[',']','<','>','''','/','|']))
  278. do begin
  279. Inc(linePos);
  280. end;
  281. if (linePos > k) then begin
  282. if (line[linePos] in [' ',#9,'#', '=','&','[',']','<','>','''','/','|']) then
  283. Result := Copy(line,k,(linePos-k))
  284. else
  285. Result := Copy(line,k,(linePos-k)); //Result := Copy(line,k,(linePos-k+1));
  286. end else begin
  287. Result := '';
  288. end;
  289. end;
  290. function NextToken() : UTF8String; overload;
  291. var
  292. k : Integer;
  293. ks : UTF8String;
  294. begin
  295. specialChararter := False;
  296. while True do begin
  297. while (linePos <= lineLength) and CharInSet(line[linePos],[' ', #9, #13]) do begin
  298. Inc(linePos);
  299. end;
  300. if (linePos > lineLength) or (line[linePos] = '#') then begin
  301. if not NextLine() then
  302. exit('');
  303. Continue;
  304. end ;
  305. Break;
  306. end;
  307. if (linePos > lineLength) then
  308. exit('');
  309. if (line[linePos] = '*') then begin
  310. linePos := linePos+1;
  311. specialChararter := True;
  312. exit('*');
  313. end;
  314. k := linePos;
  315. if (linePos <= lineLength) and CharInSet(line[linePos],['<','>']) then begin
  316. ks := line[linePos];
  317. while (linePos <= lineLength) and (line[linePos] = ks) do begin
  318. Inc(linePos);
  319. end;
  320. Result := Copy(line,k,(linePos-k));
  321. exit;
  322. end;
  323. if (linePos <= lineLength) and
  324. CharInSet(line[linePos],['=','&','[',']','<','>','/','|'])
  325. then begin
  326. Inc(linePos);
  327. Result := Copy(line,k,(linePos-k));
  328. specialChararter := True;
  329. exit;
  330. end;
  331. {if (line[linePos] = '''') then
  332. exit(ReadQuotedString()); }
  333. Result := '';
  334. while (linePos <= lineLength) do begin
  335. if CharInSet(line[linePos],[' ',#9,#13,'#', '=','&','[',']','<','>','/','|']) then
  336. Break;
  337. if (line[linePos] <> '''') then
  338. ks := ReadUnQuotedString()
  339. else
  340. ks := ReadQuotedString();
  341. if (ks = '') then
  342. Break;
  343. Result := Result + ks;
  344. end;
  345. end;
  346. function NextToken(const AMustSucceed : Boolean) : UTF8String; overload;
  347. begin
  348. Result := NextToken();
  349. if (Result = '') and AMustSucceed then
  350. raise Exception.CreateFmt('Unexpected end of line(%d) : "%s".',[lineIndex,CurrentLine()]);
  351. end;
  352. procedure CheckToken(const AActual, AExpectedToken : UTF8String);
  353. begin
  354. if (AActual <> AExpectedToken) then
  355. raise Exception.CreateFmt(
  356. '"%s" expected but "%s" found at position %d, BufferPosition(%d), line(%d) = "%s".',
  357. [AExpectedToken,AActual,linePos,bufferPos,lineIndex,CurrentLine()]
  358. );
  359. end;
  360. function parse_reset() : Boolean;
  361. var
  362. s, s1 : UTF8String;
  363. logicalPos : TReorderLogicalReset;
  364. k : Integer;
  365. begin
  366. s := NextToken();
  367. if (s = '') then
  368. exit(False);
  369. CheckToken(s,'&');
  370. s := NextToken(True);
  371. if (s = '[') then begin
  372. s := NextToken();
  373. if (s = s_BEFORE) then begin
  374. s := NextToken();
  375. if not(TryStrToInt(s,k)) or (k < 1) or (k > 3) then
  376. CheckToken(s,'"1" or "2" or "3"');
  377. CheckToken(NextToken(True),']');
  378. statement^.Reset := String2UnicodeCodePointArray(NextToken(True));
  379. statement^.Before := True;
  380. end else begin
  381. while True do begin
  382. s1 := NextToken();
  383. if (s1 = '') or (s1 = ']') then
  384. break;
  385. s := s + Trim(s1)
  386. end;
  387. CheckToken(s1,']');
  388. if (s = '') then
  389. raise Exception.CreateFmt('Unexpected end of line : "%s".',[CurrentLine()]);
  390. if not TryStrToLogicalReorder(s,logicalPos) then
  391. raise Exception.CreateFmt(sUnknownResetLogicalPosition,[s]);
  392. statement^.LogicalPosition := logicalPos;
  393. end;
  394. end else begin
  395. statement^.Reset := String2UnicodeCodePointArray(s);
  396. end;
  397. if (statement^.LogicalPosition = TReorderLogicalReset.None) and
  398. (Length(statement^.Reset) = 0)
  399. then
  400. raise Exception.Create(sInvalidResetClause);
  401. Result := True;
  402. end;
  403. procedure EnsureElementLength(const ALength : Integer);
  404. var
  405. k, d : Integer;
  406. begin
  407. k := Length(statement^.Elements);
  408. if (k < ALength) then begin
  409. k := ALength;
  410. if (k = 0) then begin
  411. k := 50;
  412. end else begin
  413. if (k < 10) then
  414. d := 10
  415. else
  416. d := 2;
  417. k := k * d;
  418. end;
  419. statement^.SetElementCount(k);
  420. end;
  421. end;
  422. procedure AddElement(
  423. const AChars : array of UCS4Char;
  424. const AWeigthKind : TReorderWeigthKind;
  425. const AContext : UTF8String
  426. );overload;
  427. var
  428. kp : PReorderUnit;
  429. kc, k : Integer;
  430. begin
  431. EnsureElementLength(elementActualCount+1);
  432. kp := @statement^.Elements[elementActualCount];
  433. kc := Length(AChars)-1;
  434. if (kc < 0) then
  435. kc := 0;
  436. SetLength(kp^.Characters,kc);
  437. for k := 0 to kc - 1 do
  438. kp^.Characters[k] := AChars[k];
  439. kp^.WeigthKind := AWeigthKind;
  440. elementActualCount := elementActualCount + 1;
  441. if (AContext <> '') then
  442. kp^.Context := String2UnicodeCodePointArray(AContext);
  443. end;
  444. procedure AddElement(
  445. const AChar : UCS4Char;
  446. const AWeigthKind : TReorderWeigthKind;
  447. const AContext : UTF8String
  448. );overload;
  449. var
  450. kp : PReorderUnit;
  451. kc, k : Integer;
  452. begin
  453. EnsureElementLength(elementActualCount+1);
  454. kp := @statement^.Elements[elementActualCount];
  455. SetLength(kp^.Characters,1);
  456. kp^.Characters[0] := AChar;
  457. kp^.WeigthKind := AWeigthKind;
  458. elementActualCount := elementActualCount + 1;
  459. if (AContext <> '') then
  460. kp^.Context := String2UnicodeCodePointArray(AContext);
  461. end;
  462. function ReadNextItem() : Boolean;
  463. var
  464. contextStr : UTF8String;
  465. w : TReorderWeigthKind;
  466. last : PReorderUnit;
  467. u4str : UCS4String;
  468. s, ts : UTF8String;
  469. expandStr : TUnicodeCodePointArray;
  470. k, kc, x : Integer;
  471. us : UnicodeString;
  472. begin
  473. contextStr := '';
  474. expandStr := nil;
  475. Result := False;
  476. SaveState();
  477. s := NextToken();
  478. if (s = '') then begin
  479. DiscardState();
  480. exit;
  481. end;
  482. if specialChararter and (s = '&') then begin
  483. RestoreState();
  484. exit;
  485. end;
  486. DiscardState();
  487. if not TryStringToReorderWeigthKind(s,w) then
  488. CheckToken(s,'Reorder Weigth');
  489. s := NextToken(True);
  490. if specialChararter then begin
  491. if (s = '[') then begin
  492. k := 1;
  493. while True do begin
  494. ts := NextToken(True);
  495. s := s + ts;
  496. if specialChararter then begin
  497. if (ts = '[') then
  498. k := k+1
  499. else if (ts = ']') then begin
  500. k := k-1;
  501. if (k = 0) then
  502. Break;
  503. end;
  504. end;
  505. end;
  506. if (Pos('variable',s) > 0) then
  507. exit(True);
  508. end else if (s = '*') then begin
  509. s := NextToken(True);
  510. us := UTF8Decode(s);
  511. u4str := UnicodeStringToUCS4String(us);
  512. kc := Length(u4str)-1;
  513. k := 0;
  514. while (k <= (kc-1)) do begin
  515. if (k > 0) and (u4str[k] = Ord('-')) then begin
  516. if (k = (kc-1)) then begin
  517. AddElement(u4str[k],w,contextStr);
  518. end else begin
  519. for x := (u4str[k-1]+1) to u4str[k+1] do
  520. AddElement(x,w,contextStr);
  521. k := k+1;
  522. end;
  523. end else begin
  524. AddElement(u4str[k],w,contextStr);
  525. end;
  526. k := k+1;
  527. end;
  528. exit(True);
  529. end;
  530. end;
  531. SaveState();
  532. ts := NextToken();
  533. if (ts = '') or not(specialChararter) then begin
  534. RestoreState();
  535. us := UTF8Decode(s);
  536. u4str := UnicodeStringToUCS4String(us);
  537. end else begin
  538. if (ts = '|') then begin
  539. DiscardState();
  540. contextStr := s;
  541. s := NextToken(True);
  542. SaveState();
  543. ts := NextToken();
  544. end;
  545. if specialChararter and (ts = '/') then begin
  546. expandStr := String2UnicodeCodePointArray(NextToken(True));
  547. DiscardState();
  548. end else begin
  549. RestoreState();
  550. end;
  551. u4str := UnicodeStringToUCS4String(UTF8Decode(s));
  552. end;
  553. AddElement(u4str,w,contextStr);
  554. if (Length(expandStr) > 0) then begin
  555. last := @statement^.Elements[elementActualCount-1];
  556. last^.ExpansionChars := expandStr;
  557. end;
  558. Result := True;
  559. end;
  560. begin
  561. Result := False;
  562. elementActualCount := 0;
  563. if (AStartPosition >= AMaxLen) then
  564. exit;
  565. historyItemIndex := -1;
  566. lineIndex := ALineCount;
  567. bufferLength := AMaxLen;
  568. bufferPos := AStartPosition;
  569. p := AData+AStartPosition;
  570. SetLength(line,LINE_LENGTH);
  571. statement := AStatement;
  572. statement^.Clear();
  573. if not NextLine() then
  574. exit;
  575. if not parse_reset() then
  576. exit;
  577. while ReadNextItem() do begin
  578. // All done in the condition
  579. end;
  580. statement^.SetElementCount(elementActualCount);
  581. if (linePos > lineLength) then
  582. linePos := lineLength;
  583. ANextPos := bufferPos-lineLength+linePos;
  584. Result := (ANextPos > AStartPosition);
  585. ALineCount := lineIndex;
  586. end;
  587. procedure ParseInitialDocument(ASequence : POrderedCharacters; ADoc : TCustomMemoryStream);
  588. var
  589. buffer : PAnsiChar;
  590. bufferLength : Integer;
  591. i, nextPost : Integer;
  592. statement : TReorderSequence;
  593. p : PReorderUnit;
  594. lineCount : Integer;
  595. begin
  596. if (ADoc.Size < 1) then
  597. exit;
  598. buffer := ADoc.Memory; //0xEF,0xBB,0xBF
  599. bufferLength := ADoc.Size;
  600. if (bufferLength >= 3) and
  601. (Byte(buffer[0]) = $EF) and
  602. (Byte(buffer[1]) = $BB) and
  603. (Byte(buffer[2]) = $BF)
  604. then begin
  605. Inc(buffer,3);
  606. Dec(bufferLength,3);
  607. end;
  608. lineCount := 0;
  609. ASequence^.Clear();
  610. SetLength(ASequence^.Data,50000);
  611. nextPost := 0;
  612. i := 0;
  613. while (i < bufferLength) do begin
  614. statement.Clear();
  615. if not ParseStatement(buffer,i,bufferLength,@statement,nextPost,lineCount) then
  616. Break;
  617. i := nextPost;
  618. try
  619. ASequence^.ApplyStatement(@statement);
  620. except
  621. on e : Exception do begin
  622. e.Message := Format('%s Position = %d',[e.Message,i]);
  623. raise;
  624. end;
  625. end;
  626. end;
  627. if (ASequence^.ActualLength > 0) then begin
  628. p := @ASequence^.Data[0];
  629. for i := 0 to ASequence^.ActualLength - 1 do begin
  630. p^.Changed := False;
  631. Inc(p);
  632. end;
  633. end;
  634. end;
  635. procedure ParseInitialDocument(ASequence : POrderedCharacters; AFileName : string);
  636. var
  637. doc : TMemoryStream;
  638. begin
  639. doc := TMemoryStream.Create();
  640. try
  641. doc.LoadFromFile(AFileName);
  642. doc.Position := 0;
  643. ParseInitialDocument(ASequence,doc);
  644. finally
  645. doc.Free();
  646. end;
  647. end;
  648. end.