{
    This file is part of the Free Component Library (FCL)
    Copyright (c) 1999-2000 by the Free Pascal development team

    See the file COPYING.FPC, included in this distribution,
    for details about the copyright.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

 **********************************************************************}

{****************************************************************************}
{*                             TParser                                      *}
{****************************************************************************}

const
  ParseBufSize     = 4096;

procedure TParser.ReadBuffer;
var
  Count            : Integer;
begin
  Inc(FOrigin, FSourcePtr - FBuffer);

  FSourceEnd[0] := FSaveChar;
  Count         := FBufPtr - FSourcePtr;
  if Count <> 0 then
  begin
    Move(FSourcePtr[0], FBuffer[0], Count);
  end;

  FBufPtr := FBuffer + Count;
  Inc(FBufPtr, FStream.Read(FBufPtr[0], FBufEnd - FBufPtr));

  FSourcePtr := FBuffer;
  FSourceEnd := FBufPtr;
  if (FSourceEnd = FBufEnd) then
  begin
    FSourceEnd := LineStart(FBuffer, FSourceEnd - 1);
    if FSourceEnd = FBuffer then
    begin
      Error(SLineTooLong);
    end;
  end;
  FSaveChar := FSourceEnd[0];
  FSourceEnd[0] := #0;
end;

procedure TParser.SkipBlanks;
begin
  while FSourcePtr^ < #33 do begin
    if FSourcePtr^ = #0 then begin
      ReadBuffer;
      if FSourcePtr^ = #0 then exit;
      continue;
    end else if FSourcePtr^ = #10 then Inc(FSourceLine);
    Inc(FSourcePtr);
  end;
end;

constructor TParser.Create(Stream: TStream);
begin
  inherited Create;

  FStream := Stream;
  GetMem(FBuffer, ParseBufSize);

  FBuffer[0]  := #0;
  FBufPtr     := FBuffer;
  FBufEnd     := FBuffer + ParseBufSize;
  FSourcePtr  := FBuffer;
  FSourceEnd  := FBuffer;
  FTokenPtr   := FBuffer;
  FSourceLine := 1;

  NextToken;
end;


destructor TParser.Destroy;
begin
  if Assigned(FBuffer) then
  begin
    FStream.Seek(PtrInt(FTokenPtr) - PtrInt(FBufPtr), 1);
    FreeMem(FBuffer, ParseBufSize);
  end;

  inherited Destroy;
end;

procedure TParser.CheckToken(T : Char);
begin
  if Token <> T then
  begin
    case T of
      toSymbol:
        Error(SIdentifierExpected);
      toString:
        Error(SStringExpected);
      toInteger, toFloat:
        Error(SNumberExpected);
    else
      ErrorFmt(SCharExpected, [T]);
    end;
  end;
end;

procedure TParser.CheckTokenSymbol(const S: string);
begin
  if not TokenSymbolIs(S) then
    ErrorFmt(SSymbolExpected, [S]);
end;

Procedure TParser.Error(const Ident: string);
begin
  ErrorStr(Ident);
end;

Procedure TParser.ErrorFmt(const Ident: string; const Args: array of const);
begin
  ErrorStr(Format(Ident, Args));
end;

Procedure TParser.ErrorStr(const Message: string);
begin
  raise EParserError.CreateFmt(SParseError, [Message, FSourceLine]);
end;


procedure TParser.HexToBinary(Stream: TStream);

  function HexDigitToInt(c: Char): Integer;
  begin
    if (c >= '0') and (c <= '9') then Result := Ord(c) - Ord('0')
    else if (c >= 'A') and (c <= 'F') then Result := Ord(c) - Ord('A') + 10
    else if (c >= 'a') and (c <= 'f') then Result := Ord(c) - Ord('a') + 10
    else Result := -1;
  end;

var
  buf: array[0..255] of Byte;
  digit1: Integer;
  bytes: Integer;
begin
  SkipBlanks;
  while FSourcePtr^ <> '}' do begin
    bytes := 0;
    while True do begin
      digit1 := HexDigitToInt(FSourcePtr[0]);
      if digit1 < 0 then break;
      buf[bytes] := digit1 shl 4 or HexDigitToInt(FSourcePtr[1]);
      Inc(FSourcePtr, 2);
      Inc(bytes);
    end;
    if bytes = 0 then Error(SInvalidBinary);
    Stream.Write(buf, bytes);
    SkipBlanks;
  end;
  NextToken;
end;


Function TParser.NextToken: Char;
var
  CharCount         : Integer;
 
  procedure PutChar(achar: Word);
  begin
    inc(CharCount);
    if length(fString) < CharCount then begin
     setlength(fString,length(fString) + length(fString) div 4 + 64);
    end;
    fString[CharCount]:= WideChar(achar);
  end;
 
var
  I                : Integer;
  P                : PChar;
begin
  SkipBlanks;
  P := FSourcePtr;
  FTokenPtr := P;
  case P^ of
    'A'..'Z', 'a'..'z', '_':
      begin
        Inc(P);
        while P^ in ['A'..'Z', 'a'..'z', '0'..'9', '_'] do Inc(P);
        Result := toSymbol;
      end;
    '#', '''':
      begin
        CharCount:= 0;
        while True do
          case P^ of
            '#':
              begin
                Inc(P);
                I := 0;
                while P^ in ['0'..'9'] do
                begin
                  I := I * 10 + (Ord(P^) - Ord('0'));
                  Inc(P);
                end;
                PutChar(I)
              end;
            '''':
              begin
                Inc(P);
                while True do
                begin
                  case P^ of
                    #0, #10, #13:
                      Error(SInvalidString);
                    '''':
                      begin
                        Inc(P);
                        if P^ <> '''' then Break;
                      end;
                  end;
                  PutChar(Word(P^));
                  Inc(P);
                end;
              end;
          else
            Break;
          end;
        setlength(fString,CharCount);
        Result := toString;
      end;
    '$':
      begin
        Inc(P);
        while P^ in ['0'..'9', 'A'..'F', 'a'..'f'] do Inc(P);
        Result := toInteger;
      end;
    '-', '0'..'9':
      begin
        Inc(P);
        while P^ in ['0'..'9'] do Inc(P);
        Result := toInteger;
        while (P^ in ['0'..'9', '.', 'e', 'E', '+', '-']) and not
          ((P[0] = '.') and not (P[1] in ['0'..'9', 'e', 'E'])) do
        begin
          Inc(P);
          Result := toFloat;
        end;
      end;
  else
    Result := P^;
    if Result <> toEOF then Inc(P);
  end;
  FSourcePtr := P;
  FToken := Result;
end;

Function TParser.SourcePos: Longint;
begin
  Result := FOrigin + (FTokenPtr - FBuffer);
end;


Function TParser.TokenComponentIdent: String;
var
  P                : PChar;
begin
  CheckToken(toSymbol);

  P := FSourcePtr;
  while P^ = '.' do
  begin
    Inc(P);
    if not (P^ in ['A'..'Z', 'a'..'z', '_']) then
      Error(SIdentifierExpected);
    repeat
      Inc(P)
    until not (P^ in ['A'..'Z', 'a'..'z', '0'..'9', '_']);
  end;
  FSourcePtr := P;
  Result := TokenString;
end;

Function TParser.TokenFloat: Extended;
var
  I,FloatError       : Integer;
  Back             : Real;
  S                : String;
  
begin
  Result   := 0;
  S:=TokenString;
  // Convert , decimal separator to ., handle backwards compatibility streams.
  for I:=1 to Length(S) do
    if (S[i]=',') then
      S[i]:='.';
  Val(S, Back, FloatError);
  Result := Back;
end;

Function TParser.TokenInt: Longint;
begin
  Result := StrToInt(TokenString);
end;

Function TParser.TokenString: string;
var
  L                : Integer;
begin
  if FToken = toString then begin
    result:= fString;
  end else begin
    L := FSourcePtr - FTokenPtr;
    SetLength(Result,L);
    If (L>0) then
      Move(FTokenPtr^,Result[1],L);
  end;
end;

Function TParser.TokenWideString: widestring;
begin
  if FToken = toString then
    result:= fString
  else
    result:= TokenString
end;


Function TParser.TokenSymbolIs(const S: string): Boolean;
begin
  Result := (Token = toSymbol) and (CompareText(S, TokenString) = 0);
end;
