1 (* coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, version 3 of the License ONLY.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 {$INCLUDE a_modes.inc}
17 {.$DEFINE XPARSER_DEBUG}
23 SysUtils
, Classes
{$IFDEF USE_MEMPOOL}, mempool
{$ENDIF};
26 // ////////////////////////////////////////////////////////////////////////// //
30 TParserException
= class(Exception
)
32 tokLine
, tokCol
: Integer;
35 constructor Create (pr
: TTextParser
; const amsg
: AnsiString);
36 constructor CreateFmt (pr
: TTextParser
; const afmt
: AnsiString; const args
: array of const);
39 TTextParser
= class{$IFDEF USE_MEMPOOL}(TPoolObject
){$ENDIF}
46 //TTFloat = 3; // not yet
48 TTDelim
= 5; // one-char delimiters
53 TTGreatEqu
= 14; // >=
55 TTEqu
= 16; // == or <>
64 SignedNumbers
, // allow signed numbers; otherwise sign will be TTDelim
65 DollarIsId
, // allow dollar in identifiers; otherwise dollar will be TTDelim
66 DotIsId
, // allow dot in identifiers; otherwise dot will be TTDelim
67 DashIsId
, // '-' can be part of identifier (but identifier cannot start with '-')
68 HtmlColors
, // #rgb or #rrggbb colors
69 PascalComments
// allow `{}` pascal comments
71 TOptions
= set of TOption
;
75 TAnsiCharSet
= set of AnsiChar;
82 mCharBuf
: packed array [0..CharBufSize
-1] of AnsiChar;
83 mCharBufUsed
: Integer;
85 mEofHit
: Boolean; // no more chars to load into mCharBuf
89 mTokLine
, mTokCol
: Integer; // token start
91 mTokStr
: AnsiString; // string or identifier
92 mTokChar
: AnsiChar; // for delimiters
96 procedure fillCharBuf ();
97 function popFrontChar (): AnsiChar; inline; // never drains char buffer (except on "total EOF")
98 function peekCurChar (): AnsiChar; inline;
99 function peekNextChar (): AnsiChar; inline;
100 function peekChar (dest
: Integer): AnsiChar; inline;
103 function loadChar (): AnsiChar; virtual; abstract; // loads next char; #0 means 'eof'
106 function isIdStartChar (ch
: AnsiChar): Boolean; inline;
107 function isIdMidChar (ch
: AnsiChar): Boolean; inline;
110 constructor Create (aopts
: TOptions
=[TOption
.SignedNumbers
]);
111 destructor Destroy (); override;
113 procedure error (const amsg
: AnsiString); noreturn
;
114 procedure errorfmt (const afmt
: AnsiString; const args
: array of const); noreturn
;
116 function skipChar (): Boolean; // returns `false` on eof
118 function skipBlanks (): Boolean; // ...and comments; returns `false` on eof
120 function skipToken (): Boolean; // returns `false` on eof
121 {$IFDEF XPARSER_DEBUG}
122 function skipToken1 (): Boolean;
125 function isEOF (): Boolean; inline;
126 function isId (): Boolean; inline;
127 function isInt (): Boolean; inline;
128 function isStr (): Boolean; inline;
129 function isDelim (): Boolean; inline;
130 function isIdOrStr (): Boolean; inline;
132 function expectId (): AnsiString;
133 procedure expectId (const aid
: AnsiString; caseSens
: Boolean=true);
134 function eatId (const aid
: AnsiString; caseSens
: Boolean=true): Boolean;
135 function eatIdOrStr (const aid
: AnsiString; caseSens
: Boolean=true): Boolean;
136 function eatIdOrStrCI (const aid
: AnsiString): Boolean; inline;
138 function expectStr (allowEmpty
: Boolean=false): AnsiString;
139 function expectInt (): Integer;
141 function expectIdOrStr (allowEmpty
: Boolean=false): AnsiString;
143 procedure expectTT (ttype
: Integer);
144 function eatTT (ttype
: Integer): Boolean;
146 procedure expectDelim (const ch
: AnsiChar);
147 function expectDelims (const ch
: TAnsiCharSet
): AnsiChar;
148 function eatDelim (const ch
: AnsiChar): Boolean;
150 function isDelim (const ch
: AnsiChar): Boolean; inline;
153 property options
: TOptions read mOptions write mOptions
;
156 property col
: Integer read mCol
;
157 property line
: Integer read mLine
;
159 property curChar
: AnsiChar read peekCurChar
;
160 property nextChar
: AnsiChar read peekNextChar
;
163 property tokCol
: Integer read mTokCol
;
164 property tokLine
: Integer read mTokLine
;
166 property tokType
: Integer read mTokType
; // see TTXXX constants
167 property tokStr
: AnsiString read mTokStr
; // string or identifier
168 property tokChar
: AnsiChar read mTokChar
; // for delimiters
169 property tokInt
: Integer read mTokInt
;
173 // ////////////////////////////////////////////////////////////////////////// //
175 TFileTextParser
= class(TTextParser
)
177 const BufSize
= 16384;
181 mStreamOwned
: Boolean;
187 function loadChar (): AnsiChar; override; // loads next char; #0 means 'eof'
190 constructor Create (const fname
: AnsiString; aopts
: TOptions
=[TOption
.SignedNumbers
]);
191 constructor Create (st
: TStream
; astOwned
: Boolean=true; aopts
: TOptions
=[TOption
.SignedNumbers
]);
192 destructor Destroy (); override;
195 TStrTextParser
= class(TTextParser
)
201 function loadChar (): AnsiChar; override; // loads next char; #0 means 'eof'
204 constructor Create (const astr
: AnsiString; aopts
: TOptions
=[TOption
.SignedNumbers
]);
205 destructor Destroy (); override;
209 // ////////////////////////////////////////////////////////////////////////// //
216 procedure putBuf (constref buf
; len
: SizeUInt
); virtual; abstract;
219 constructor Create ();
221 procedure flush (); virtual;
223 procedure put (const s
: AnsiString); overload
;
224 procedure put (v
: Byte); overload
;
225 procedure put (v
: Integer); overload
;
226 procedure put (const fmt
: AnsiString; args
: array of const); overload
;
227 procedure putIndent ();
229 procedure unindent ();
232 property curIndent
: Integer read mIndent
;
236 // ////////////////////////////////////////////////////////////////////////// //
238 TFileTextWriter
= class(TTextWriter
)
240 const BufSize
= 16384;
244 mStreamOwned
: Boolean;
249 procedure putBuf (constref buf
; len
: SizeUInt
); override;
252 constructor Create (const fname
: AnsiString);
253 constructor Create (ast
: TStream
; astOwned
: Boolean=true); // will own the stream by default
254 destructor Destroy (); override;
256 procedure flush (); override;
259 TStrTextWriter
= class(TTextWriter
)
264 procedure putBuf (constref buf
; len
: SizeUInt
); override;
267 constructor Create ();
268 destructor Destroy (); override;
270 property str
: AnsiString read mStr
;
280 // ////////////////////////////////////////////////////////////////////////// //
281 constructor TParserException
.Create (pr
: TTextParser
; const amsg
: AnsiString);
283 if (pr
<> nil) then begin tokLine
:= pr
.tokLine
; tokCol
:= pr
.tokCol
; end;
284 inherited Create(amsg
);
287 constructor TParserException
.CreateFmt (pr
: TTextParser
; const afmt
: AnsiString; const args
: array of const);
289 if (pr
<> nil) then begin tokLine
:= pr
.tokLine
; tokCol
:= pr
.tokCol
; end;
290 inherited Create(formatstrf(afmt
, args
));
294 // ////////////////////////////////////////////////////////////////////////// //
295 constructor TTextParser
.Create (aopts
: TOptions
=[TOption
.SignedNumbers
]);
310 if (mBufLen >= 3) and (mBuffer[0] = #$EF) and (mBuffer[1] = #$BB) and (mBuffer[2] = #$BF) then
312 for f := 3 to mBufLen-1 do mBuffer[f-3] := mBuffer[f];
319 destructor TTextParser
.Destroy ();
325 procedure TTextParser
.error (const amsg
: AnsiString); noreturn
;
327 raise TParserException
.Create(self
, amsg
);
331 procedure TTextParser
.errorfmt (const afmt
: AnsiString; const args
: array of const); noreturn
;
333 raise TParserException
.CreateFmt(self
, afmt
, args
);
337 function TTextParser
.isIdStartChar (ch
: AnsiChar): Boolean; inline;
341 ((ch
>= 'A') and (ch
<= 'Z')) or
342 ((ch
>= 'a') and (ch
<= 'z')) or
344 ((ch
= '$') and (TOption
.DollarIsId
in mOptions
)) or
345 ((ch
= '.') and (TOption
.DotIsId
in mOptions
));
348 function TTextParser
.isIdMidChar (ch
: AnsiChar): Boolean; inline;
351 ((ch
>= '0') and (ch
<= '9')) or
352 ((ch
= '-') and (TOption
.DashIsId
in mOptions
)) or
357 procedure TTextParser
.fillCharBuf ();
361 if (mEofHit
) then begin mCharBuf
[mCharBufPos
] := #0; exit
; end;
362 while (not mEofHit
) and (mCharBufUsed
< CharBufSize
) do
365 mCharBuf
[(mCharBufPos
+mCharBufUsed
) mod CharBufSize
] := ch
;
366 if (ch
= #0) then begin mEofHit
:= true; break
; end;
372 // never drains char buffer (except on "total EOF")
373 function TTextParser
.popFrontChar (): AnsiChar; inline;
375 if (mEofHit
) and (mCharBufUsed
= 0) then begin result
:= #0; exit
; end;
376 assert(mCharBufUsed
> 0);
377 result
:= mCharBuf
[mCharBufPos
];
378 mCharBufPos
:= (mCharBufPos
+1) mod CharBufSize
;
380 if (not mEofHit
) and (mCharBufUsed
= 0) then fillCharBuf();
383 function TTextParser
.peekCurChar (): AnsiChar; inline;
385 if (mCharBufUsed
= 0) and (not mEofHit
) then fillCharBuf();
386 result
:= mCharBuf
[mCharBufPos
]; // it is safe, 'cause `fillCharBuf()` will put #0 on "total EOF"
389 function TTextParser
.peekNextChar (): AnsiChar; inline;
391 if (mCharBufUsed
< 2) and (not mEofHit
) then fillCharBuf();
392 if (mCharBufUsed
< 2) then result
:= #0 else result
:= mCharBuf
[(mCharBufPos
+1) mod CharBufSize
];
395 function TTextParser
.peekChar (dest
: Integer): AnsiChar; inline;
397 if (dest
< 0) or (dest
>= CharBufSize
) then error('internal text parser error');
398 if (mCharBufUsed
< dest
+1) then fillCharBuf();
399 if (mCharBufUsed
< dest
+1) then result
:= #0 else result
:= mCharBuf
[(mCharBufPos
+dest
) mod CharBufSize
];
403 function TTextParser
.skipChar (): Boolean;
407 ch
:= popFrontChar();
408 if (ch
= #0) then begin result
:= false; exit
; end;
421 if (mCharBufUsed
> 0) and (mCharBuf
[0] = #10) then
423 if (popFrontChar() = #0) then result
:= false;
432 function TTextParser
.skipBlanks (): Boolean;
436 //writeln('line=', mLine, '; col=', mCol, '; char0=', Integer(peekChar(0)));
437 if (mLine
= 1) and (mCol
= 1) and
438 (peekChar(0) = #
$EF) and
439 (peekChar(1) = #
$BB) and
440 (peekChar(2) = #
$BF) then
447 while (curChar
<> #0) do
449 if (curChar
= '/') then
451 // single-line comment
452 if (nextChar
= '/') then
454 //writeln('spos=(', mLine, ',', mCol, ')');
455 while (curChar
<> #0) and (curChar
<> #10) and (curChar
<> #13) do skipChar();
456 skipChar(); // skip EOL
457 //writeln('{', curChar, '}');
458 //writeln('epos=(', mLine, ',', mCol, ')');
462 if (nextChar
= '*') then
464 // skip comment start
467 while (curChar
<> #0) do
469 if (curChar
= '*') and (nextChar
= '/') then
480 // nesting multline comment
481 if (nextChar
= '+') then
483 // skip comment start
487 while (curChar
<> #0) do
489 if (curChar
= '+') and (nextChar
= '/') then
495 if (level
= 0) then break
;
498 if (curChar
= '/') and (nextChar
= '+') then
500 // skip comment start
511 else if (curChar
= '(') and (nextChar
= '*') then
513 // pascal comment; skip comment start
516 while (curChar
<> #0) do
518 if (curChar
= '*') and (nextChar
= ')') then
529 else if (curChar
= '{') and (TOption
.PascalComments
in mOptions
) then
531 // pascal comment; skip comment start
533 while (curChar
<> #0) do
535 if (curChar
= '}') then
545 if (curChar
> ' ') then break
;
546 skipChar(); // skip blank
548 result
:= (curChar
<> #0);
552 {$IFDEF XPARSER_DEBUG}
553 function TTextParser
.skipToken (): Boolean;
555 writeln('getting token...');
556 result
:= skipToken1();
557 writeln(' got token: ', mTokType
, ' <', mTokStr
, '> : <', mTokChar
, '>');
560 function TTextParser
.skipToken1 (): Boolean;
562 function TTextParser
.skipToken (): Boolean;
564 procedure parseInt ();
566 neg
: Boolean = false;
570 if (TOption
.SignedNumbers
in mOptions
) then
572 if (curChar
= '+') or (curChar
= '-') then
574 neg
:= (curChar
= '-');
576 if (curChar
< '0') or (curChar
> '9') then
579 if (neg
) then mTokChar
:= '-' else mTokChar
:= '+';
584 if (curChar
= '0') then
600 if (base
< 0) then base
:= 10;
601 if (digitInBase(curChar
, base
) < 0) then error('invalid number');
603 mTokInt
:= 0; // just in case
604 while (curChar
<> #0) do
606 if (curChar
= '_') then
609 if (curChar
= #0) then break
;
611 n
:= digitInBase(curChar
, base
);
612 if (n
< 0) then break
;
614 if (n
< 0) or (n
< mTokInt
) then error('integer overflow');
618 // check for valid number end
619 if (curChar
<> #0) then
621 if (curChar
= '.') then error('floating numbers aren''t supported yet');
622 if (isIdMidChar(curChar
)) then error('invalid number');
624 if neg
then mTokInt
:= -mTokInt
;
627 procedure parseString ();
633 mTokStr
:= ''; // just in case
635 skipChar(); // skip starting quote
636 while (curChar
<> #0) do
639 if (qch
= '"') and (curChar
= '\') then
641 if (nextChar
= #0) then error('unterminated string escape');
643 // skip backslash and escape type
652 'x', 'X': // hex escape
654 n
:= digitInBase(curChar
, 16);
655 if (n
< 0) then error('invalid hexstr escape');
657 if (digitInBase(curChar
, 16) > 0) then
659 n
:= n
*16+digitInBase(curChar
, 16);
662 mTokStr
+= AnsiChar(n
);
668 // duplicate single quote (pascal style)
669 if (qch
= '''') and (curChar
= '''') and (nextChar
= '''') then
677 if (curChar
= qch
) then
679 skipChar(); // skip ending quote
687 procedure parseId ();
690 mTokStr
:= ''; // just in case
691 while (isIdMidChar(curChar
)) do
693 if (curChar
= '.') and (nextChar
= '.') then break
; // dotdot is a token by itself
707 if not skipBlanks() then
722 if (TOption
.SignedNumbers
in mOptions
) and ((curChar
= '+') or (curChar
= '-')) then begin parseInt(); exit
; end;
723 if (curChar
>= '0') and (curChar
<= '9') then begin parseInt(); exit
; end;
726 if (curChar
= '"') or (curChar
= '''') or (curChar
= '`') then begin parseString(); exit
; end;
729 if (curChar
= '#') and (TOption
.HtmlColors
in mOptions
) then
731 if (digitInBase(peekChar(1), 16) >= 0) and (digitInBase(peekChar(2), 16) >= 0) and (digitInBase(peekChar(3), 16) >= 0) then
733 if (digitInBase(peekChar(4), 16) >= 0) and (digitInBase(peekChar(5), 16) >= 0) and (digitInBase(peekChar(6), 16) >= 0) then xpos
:= 7 else xpos
:= 4;
734 if (not isIdMidChar(peekChar(xpos
))) then
750 if (isIdStartChar(curChar
)) then
752 if (curChar
= '.') and (nextChar
= '.') then
754 // nothing to do here, as dotdot is a token by itself
767 if (curChar
= '=') then
770 '<': begin mTokType
:= TTLessEqu
; mTokStr
:= '<='; skipChar(); exit
; end;
771 '>': begin mTokType
:= TTGreatEqu
; mTokStr
:= '>='; skipChar(); exit
; end;
772 '!': begin mTokType
:= TTNotEqu
; mTokStr
:= '!='; skipChar(); exit
; end;
773 '=': begin mTokType
:= TTEqu
; mTokStr
:= '=='; skipChar(); exit
; end;
774 ':': begin mTokType
:= TTAss
; mTokStr
:= ':='; skipChar(); exit
; end;
777 else if (mTokChar
= curChar
) then
780 '<': begin mTokType
:= TTShl
; mTokStr
:= '<<'; skipChar(); exit
; end;
781 '>': begin mTokType
:= TTShr
; mTokStr
:= '>>'; skipChar(); exit
; end;
782 '&': begin mTokType
:= TTLogAnd
; mTokStr
:= '&&'; skipChar(); exit
; end;
783 '|': begin mTokType
:= TTLogOr
; mTokStr
:= '||'; skipChar(); exit
; end;
789 '<': if (curChar
= '>') then begin mTokType
:= TTNotEqu
; mTokStr
:= '<>'; skipChar(); exit
; end;
790 '.': if (curChar
= '.') then begin mTokType
:= TTDotDot
; mTokStr
:= '..'; skipChar(); exit
; end;
796 function TTextParser
.isEOF (): Boolean; inline; begin result
:= (mTokType
= TTEOF
); end;
797 function TTextParser
.isId (): Boolean; inline; begin result
:= (mTokType
= TTId
); end;
798 function TTextParser
.isInt (): Boolean; inline; begin result
:= (mTokType
= TTInt
); end;
799 function TTextParser
.isStr (): Boolean; inline; begin result
:= (mTokType
= TTStr
); end;
800 function TTextParser
.isDelim (): Boolean; inline; begin result
:= (mTokType
= TTDelim
); end;
801 function TTextParser
.isIdOrStr (): Boolean; inline; begin result
:= (mTokType
= TTId
) or (mTokType
= TTStr
); end;
804 function TTextParser
.expectId (): AnsiString;
806 if (mTokType
<> TTId
) then error('identifier expected');
812 procedure TTextParser
.expectId (const aid
: AnsiString; caseSens
: Boolean=true);
816 if (mTokType
<> TTId
) or (mTokStr
<> aid
) then error('identifier '''+aid
+''' expected');
820 if (mTokType
<> TTId
) or (not strEquCI1251(mTokStr
, aid
)) then error('identifier '''+aid
+''' expected');
826 function TTextParser
.eatId (const aid
: AnsiString; caseSens
: Boolean=true): Boolean;
830 result
:= (mTokType
= TTId
) and (mTokStr
= aid
);
834 result
:= (mTokType
= TTId
) and strEquCI1251(mTokStr
, aid
);
836 if result
then skipToken();
840 function TTextParser
.eatIdOrStr (const aid
: AnsiString; caseSens
: Boolean=true): Boolean;
844 result
:= (mTokType
= TTId
) and (mTokStr
= aid
);
845 if not result
then result
:= (mTokType
= TTStr
) and (mTokStr
= aid
);
849 result
:= (mTokType
= TTId
) and strEquCI1251(mTokStr
, aid
);
850 if not result
then result
:= (mTokType
= TTStr
) and strEquCI1251(mTokStr
, aid
);
852 if result
then skipToken();
856 function TTextParser
.eatIdOrStrCI (const aid
: AnsiString): Boolean; inline;
858 result
:= eatIdOrStr(aid
, false);
862 function TTextParser
.expectStr (allowEmpty
: Boolean=false): AnsiString;
864 if (mTokType
<> TTStr
) then error('string expected');
865 if (not allowEmpty
) and (Length(mTokStr
) = 0) then error('non-empty string expected');
871 function TTextParser
.expectIdOrStr (allowEmpty
: Boolean=false): AnsiString;
875 if (not allowEmpty
) and (Length(mTokStr
) = 0) then error('non-empty string expected');
879 error('string or identifier expected');
886 function TTextParser
.expectInt (): Integer;
888 if (mTokType
<> TTInt
) then error('string expected');
894 procedure TTextParser
.expectTT (ttype
: Integer);
896 if (mTokType
<> ttype
) then error('unexpected token');
901 function TTextParser
.eatTT (ttype
: Integer): Boolean;
903 result
:= (mTokType
= ttype
);
904 if result
then skipToken();
908 procedure TTextParser
.expectDelim (const ch
: AnsiChar);
910 if (mTokType
<> TTDelim
) or (mTokChar
<> ch
) then errorfmt('delimiter ''%s'' expected', [ch
]);
915 function TTextParser
.expectDelims (const ch
: TAnsiCharSet
): AnsiChar;
917 if (mTokType
<> TTDelim
) then error('delimiter expected');
918 if not (mTokChar
in ch
) then error('delimiter expected');
924 function TTextParser
.eatDelim (const ch
: AnsiChar): Boolean;
926 result
:= (mTokType
= TTDelim
) and (mTokChar
= ch
);
927 if result
then skipToken();
931 function TTextParser
.isDelim (const ch
: AnsiChar): Boolean; inline;
933 result
:= (mTokType
= TTDelim
) and (mTokChar
= ch
);
937 // ////////////////////////////////////////////////////////////////////////// //
938 constructor TFileTextParser
.Create (const fname
: AnsiString; aopts
: TOptions
=[TOption
.SignedNumbers
]);
941 mFile
:= openDiskFileRO(fname
);
942 mStreamOwned
:= true;
943 GetMem(mBuffer
, BufSize
);
945 mBufLen
:= mFile
.Read(mBuffer
^, BufSize
);
946 if (mBufLen
< 0) then error('TFileTextParser: read error');
947 inherited Create(aopts
);
951 constructor TFileTextParser
.Create (st
: TStream
; astOwned
: Boolean=true; aopts
: TOptions
=[TOption
.SignedNumbers
]);
953 if (st
= nil) then error('cannot create parser for nil stream');
955 mStreamOwned
:= astOwned
;
956 GetMem(mBuffer
, BufSize
);
958 mBufLen
:= mFile
.Read(mBuffer
^, BufSize
);
959 if (mBufLen
< 0) then error('TFileTextParser: read error');
960 inherited Create(aopts
);
964 destructor TFileTextParser
.Destroy ();
966 if (mBuffer
<> nil) then FreeMem(mBuffer
);
970 if (mStreamOwned
) then FreeAndNil(mFile
) else mFile
:= nil;
975 function TFileTextParser
.loadChar (): AnsiChar;
977 if (mBufLen
= 0) then begin result
:= #0; exit
; end;
978 if (mBufPos
>= mBufLen
) then
980 mBufLen
:= mFile
.Read(mBuffer
^, BufSize
);
981 if (mBufLen
< 0) then error('TFileTextParser: read error');
982 if (mBufLen
= 0) then begin result
:= #0; exit
; end;
985 assert(mBufPos
< mBufLen
);
986 result
:= mBuffer
[mBufPos
];
988 if (result
= #0) then result
:= ' ';
992 // ////////////////////////////////////////////////////////////////////////// //
993 constructor TStrTextParser
.Create (const astr
: AnsiString; aopts
: TOptions
=[TOption
.SignedNumbers
]);
997 inherited Create(aopts
);
1001 destructor TStrTextParser
.Destroy ();
1008 function TStrTextParser
.loadChar (): AnsiChar;
1011 if (mPos
> Length(mStr
)) then exit
;
1012 result
:= mStr
[mPos
];
1014 if (result
= #0) then result
:= ' ';
1018 // ////////////////////////////////////////////////////////////////////////// //
1019 constructor TTextWriter
.Create (); begin mIndent
:= 0; end;
1020 procedure TTextWriter
.flush (); begin end;
1021 procedure TTextWriter
.put (const s
: AnsiString); overload
; begin if (Length(s
) > 0) then putBuf((@(s
[1]))^, Length(s
)); end;
1022 procedure TTextWriter
.put (v
: Byte); overload
; begin put('%d', [v
]); end;
1023 procedure TTextWriter
.put (v
: Integer); overload
; begin put('%d', [v
]); end;
1024 procedure TTextWriter
.put (const fmt
: AnsiString; args
: array of const); overload
; begin put(formatstrf(fmt
, args
)); end;
1025 procedure TTextWriter
.putIndent (); var f
: Integer; begin for f
:= 1 to mIndent
do put(' '); end;
1026 procedure TTextWriter
.indent (); begin Inc(mIndent
, 2); end;
1027 procedure TTextWriter
.unindent (); begin Dec(mIndent
, 2); end;
1030 // ////////////////////////////////////////////////////////////////////////// //
1031 constructor TFileTextWriter
.Create (const fname
: AnsiString);
1033 mFile
:= createDiskFile(fname
);
1034 mStreamOwned
:= true;
1036 GetMem(mBuffer
, BufSize
);
1037 assert(mBuffer
<> nil);
1042 constructor TFileTextWriter
.Create (ast
: TStream
; astOwned
: Boolean=true);
1044 if (ast
= nil) then raise Exception
.Create('cannot write to nil stream');
1046 mStreamOwned
:= astOwned
;
1048 GetMem(mBuffer
, BufSize
);
1049 assert(mBuffer
<> nil);
1053 destructor TFileTextWriter
.Destroy ();
1056 if (mBuffer
<> nil) then FreeMem(mBuffer
);
1059 if (mStreamOwned
) then mFile
.Free();
1065 procedure TFileTextWriter
.flush ();
1067 if (mFile
<> nil) and (mBufUsed
> 0) then
1069 mFile
.WriteBuffer(mBuffer
^, mBufUsed
);
1075 procedure TFileTextWriter
.putBuf (constref buf
; len
: SizeUInt
);
1080 if (len
= 0) then exit
;
1084 left
:= BufSize
-mBufUsed
;
1088 left
:= BufSize
-mBufUsed
;
1091 if (left
> len
) then left
:= Integer(len
);
1092 Move(pc
^, (mBuffer
+mBufUsed
)^, left
);
1093 Inc(mBufUsed
, left
);
1100 // ////////////////////////////////////////////////////////////////////////// //
1101 constructor TStrTextWriter
.Create ();
1107 destructor TStrTextWriter
.Destroy ();
1114 procedure TStrTextWriter
.putBuf (constref buf
; len
: SizeUInt
);
1116 st
: AnsiString = '';
1120 SetLength(st
, Integer(len
));
1121 Move(buf
, PChar(st
)^, Integer(len
));