* Patch from Mattias Gaertner

- fixed writing UTF-8.
  - indent multi line literals, used by asm blocks
  - fixed writing (a+b).c

git-svn-id: trunk@35502 -
This commit is contained in:
michael 2017-03-01 17:36:35 +00:00
parent 14ba99ea09
commit 2fbe76532f

View File

@ -20,7 +20,7 @@ unit jswriter;
interface
uses
{Classes, } SysUtils, jstoken, jsbase, jstree;
SysUtils, jstoken, jsbase, jstree;
Type
@ -31,7 +31,7 @@ Type
Function DoWrite(Const S : AnsiString) : Integer; virtual; abstract;
Function DoWrite(Const S : UnicodeString) : Integer; virtual; abstract;
Public
// All functions return the numberof bytes copied to output stream.
// All functions return the number of bytes copied to output stream.
Function Write(Const S : UnicodeString) : Integer;
Function Write(Const S : AnsiString) : Integer;
Function WriteLn(Const S : AnsiString) : Integer;
@ -58,6 +58,7 @@ Type
end;
{ TBufferWriter }
TBytes = Array of byte;
TBufferWriter = Class(TTextWriter)
private
@ -157,8 +158,7 @@ Type
Procedure WritePrimaryExpression(El: TJSPrimaryExpression);virtual;
Procedure WriteBinary(El: TJSBinary);virtual;
Public
Function EscapeString(const S: TJSString; Quote: TJSEscapeQuote = jseqDouble): String;
Function JSStringToStr(const S: TJSString): string;
Function EscapeString(const S: TJSString; Quote: TJSEscapeQuote = jseqDouble): TJSString;
Constructor Create(AWriter : TTextWriter);
Constructor Create(Const AFileName : String);
Destructor Destroy; override;
@ -172,12 +172,31 @@ Type
end;
EJSWriter = Class(Exception);
Function UTF16ToUTF8(const S: UnicodeString): string;
implementation
Resourcestring
SErrUnknownJSClass = 'Unknown javascript element class : %s';
SErrNilNode = 'Nil node in Javascript';
function HexDump(p: PChar; Count: integer): string;
var
i: Integer;
begin
Result:='';
for i:=0 to Count-1 do
Result:=Result+HexStr(ord(p[i]),2);
end;
function UTF16ToUTF8(const S: UnicodeString): string;
begin
Result:=UTF8Encode(S);
// prevent UTF8 codepage appear in the strings - we don't need codepage
// conversion magic
SetCodePage(RawByteString(Result), CP_ACP, False);
end;
{ TBufferWriter }
function TBufferWriter.GetBufferLength: Integer;
@ -332,13 +351,13 @@ end;
procedure TJSWriter.Write(const U: UnicodeString);
Var
S : UTF8String;
S : String;
begin
WriteIndent;
if UseUTF8 then
begin
S:=UTF8Encode(U);
S:=UTF16ToUTF8(U);
FLinePos:=FLinePos+Writer.Write(S);
end
else
@ -370,12 +389,12 @@ end;
procedure TJSWriter.WriteLn(const U: UnicodeString);
Var
S : UTF8String;
S : String;
begin
if UseUTF8 then
begin
S:=UTF8Encode(U);
S:=UTF16ToUTF8(U);
Writeln(S);
end
else
@ -387,81 +406,153 @@ begin
end;
function TJSWriter.EscapeString(const S: TJSString; Quote: TJSEscapeQuote
): String;
): TJSString;
Var
I,J,L : Integer;
P : TJSPChar;
R: TJSString;
begin
I:=1;
J:=1;
Result:='';
R:='';
L:=Length(S);
P:=TJSPChar(S);
While I<=L do
begin
if (P^ in [#0..#31,'"','''','/','\']) then
begin
Result:=Result+JSStringToStr(Copy(S,J,I-J));
R:=R+Copy(S,J,I-J);
Case P^ of
'\' : Result:=Result+'\\';
'/' : Result:=Result+'\/';
'"' : if Quote=jseqSingle then Result:=Result+'"' else Result:=Result+'\"';
'''': if Quote=jseqDouble then Result:=Result+'''' else Result:=Result+'\''';
#0..#7,#11,#14..#31: Result:=Result+'\x'+hexStr(ord(P^),2);
#8 : Result:=Result+'\b';
#9 : Result:=Result+'\t';
#10 : Result:=Result+'\n';
#12 : Result:=Result+'\f';
#13 : Result:=Result+'\r';
'\' : R:=R+'\\';
'/' : R:=R+'\/';
'"' : if Quote=jseqSingle then R:=R+'"' else R:=R+'\"';
'''': if Quote=jseqDouble then R:=R+'''' else R:=R+'\''';
#0..#7,#11,#14..#31: R:=R+'\x'+TJSString(hexStr(ord(P^),2));
#8 : R:=R+'\b';
#9 : R:=R+'\t';
#10 : R:=R+'\n';
#12 : R:=R+'\f';
#13 : R:=R+'\r';
end;
J:=I+1;
end;
Inc(I);
Inc(P);
end;
Result:=Result+JSStringToStr(Copy(S,J,I-1));
end;
function TJSWriter.JSStringToStr(const S: TJSString): string;
begin
if UseUTF8 then
Result:=UTF8Encode(S)
else
Result:=String(S);
R:=R+Copy(S,J,I-1);
Result:=R;
end;
procedure TJSWriter.WriteValue(V: TJSValue);
const
TabWidth = 4;
function GetLineIndent(var p: PWideChar): integer;
var
h: PWideChar;
begin
h:=p;
Result:=0;
repeat
case h^ of
#0: break;
#9: Result:=Result+(TabWidth-Result mod TabWidth);
' ': inc(Result);
else break;
end;
inc(h);
until false;
p:=h;
end;
function SkipToNextLineStart(p: PWideChar): PWideChar;
begin
repeat
case p^ of
#0: break;
#10,#13:
begin
if (p[1] in [#10,#13]) and (p^<>p[1]) then
inc(p,2)
else
inc(p);
break;
end
else inc(p);
end;
until false;
Result:=p;
end;
Var
S : String;
JS: TJSString;
p, StartP: PWideChar;
MinIndent, CurLineIndent: Integer;
begin
if V.CustomValue<>'' then
S:=JSStringToStr(V.CustomValue)
else
Case V.ValueType of
jstUNDEFINED : S:='undefined';
jstNull : s:='null';
jstBoolean : if V.AsBoolean then s:='true' else s:='false';
jstString :
begin
JS:=V.AsString;
if Pos('"',JS)>0 then
S:=''''+EscapeString(JS,jseqSingle)+''''
else
S:='"'+EscapeString(JS,jseqDouble)+'"';
end;
jstNumber :
if Frac(V.AsNumber)=0 then // this needs to be improved
Str(Round(V.AsNumber),S)
else
Str(V.AsNumber,S);
jstObject : ;
jstReference : ;
JSTCompletion : ;
begin
JS:=V.CustomValue;
if JS='' then exit;
p:=SkipToNextLineStart(PWideChar(JS));
if p^=#0 then
begin
// simple value
Write(JS);
exit;
end;
// multi line value
// find minimum indent
MinIndent:=-1;
repeat
CurLineIndent:=GetLineIndent(p);
if (MinIndent<0) or (MinIndent>CurLineIndent) then
MinIndent:=CurLineIndent;
p:=SkipToNextLineStart(p);
until p^=#0;
// write value lines indented
p:=PWideChar(JS);
GetLineIndent(p); // the first line is already indented, skip
repeat
StartP:=p;
p:=SkipToNextLineStart(StartP);
Write(copy(JS,StartP-PWideChar(JS)+1,p-StartP));
if p^=#0 then break;
CurLineIndent:=GetLineIndent(p);
Write(StringOfChar(FIndentChar,FCurIndent+CurLineIndent-MinIndent));
until false;
exit;
end;
Case V.ValueType of
jstUNDEFINED : S:='undefined';
jstNull : s:='null';
jstBoolean : if V.AsBoolean then s:='true' else s:='false';
jstString :
begin
JS:=V.AsString;
if Pos('"',JS)>0 then
JS:=''''+EscapeString(JS,jseqSingle)+''''
else
JS:='"'+EscapeString(JS,jseqDouble)+'"';
Write(JS);
exit;
end;
jstNumber :
if Frac(V.AsNumber)=0 then // this needs to be improved
Str(Round(V.AsNumber),S)
else
Str(V.AsNumber,S);
jstObject : ;
jstReference : ;
JSTCompletion : ;
end;
Write(S);
end;
@ -680,10 +771,24 @@ end;
procedure TJSWriter.WriteMemberExpression(El: TJSMemberExpression);
var
MExpr: TJSElement;
begin
if El is TJSNewMemberExpression then
Write('new ');
WriteJS(El.MExpr);
MExpr:=El.MExpr;
if (MExpr is TJSPrimaryExpression)
or (MExpr is TJSDotMemberExpression)
or (MExpr is TJSBracketMemberExpression)
or (MExpr is TJSCallExpression)
or (MExpr is TJSLiteral) then
WriteJS(MExpr)
else
begin
Write('(');
WriteJS(MExpr);
Write(')');
end;
if El is TJSDotMemberExpression then
begin
write('.');
@ -1309,23 +1414,23 @@ begin
Result:=DoWrite(S);
end;
Function TTextWriter.Write(Const S: String) : integer;
Function TTextWriter.Write(Const S: AnsiString) : integer;
begin
Result:=DoWrite(S);
end;
Function TTextWriter.WriteLn(Const S: String) : Integer;
Function TTextWriter.WriteLn(Const S: AnsiString) : Integer;
begin
Result:=DoWrite(S)+DoWrite(sLineBreak);
end;
Function TTextWriter.Write(Const Fmt: String; Args: Array of const) : Integer;
Function TTextWriter.Write(Const Fmt: AnsiString; Args: Array of const) : Integer;
begin
Result:=DoWrite(Format(Fmt,Args));
end;
Function TTextWriter.WriteLn(Const Fmt: String; Args: Array of const) : integer;
Function TTextWriter.WriteLn(Const Fmt: AnsiString; Args: Array of const) : integer;
begin
Result:=WriteLn(Format(Fmt,Args));
end;