{*_* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

Author:       Albert Drent
Description:  ASGRout parser routines
Creation:     Januari 1998
Version:      1.2.B
EMail:        a.drent@aducom.com (www.aducom.com)
Support:      support@aducom.com (www.aducom.com)
Legal issues: Copyright (C) 2003 by Aducom Software

              Aducom Software
              Eckhartstr 61
              9746 BN  Groningen
              Netherlands

              This software is provided 'as-is', without any express or
              implied warranty.  In no event will the author be held liable
              for any damages arising from the use of this software.

              Permission is granted to anyone to use this software for any
              purpose, including commercial applications, and to alter it
              and redistribute it freely, subject to the following
              restrictions:

              1. The origin of this software must not be misrepresented,
                 you must not claim that you wrote the original software.
                 If you use this software in a product, an acknowledgment
                 in the product documentation would be appreciated but is
                 not required.

              2. Altered source versions must be plainly marked as such, and
                 must not be misrepresented as being the original software.

              3. If you make changes which improves the component you must
                 mail these to aducom as the moderator of the components
                 complete with documentation for the benefits of the community.

              4. You are not allowed to create commercial available components
                 using this software. If you use this source in any way to create
                 your own components, your source should be free of charge,
                 available to anyone. It's a far better idea to distribute your
                 changes through Aducom Software.

              5. This notice may not be removed or altered from any source
                 distribution.

              6. You must register this software by entering the support forum.
                 I like to keep track about where the components are used, so
                 sending a picture postcard to the author would be appreciated.
                 Use a nice stamp and mention your name, street
                 address, EMail address and any comment you like to say.

Modifications
              26/5/2004 Function YYYYMMDDParser by JPierce, necessary for
              locale independent datehandling in SQLite components.
*_* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * }

unit ASGRout;

interface

uses SysUtils;

const
  vtcIdentifier = 1;
  vtcNumber = 2;
  vtcAssignment = 3;
  vtcQString = 4;
  vtcDString = 5;
  vtcRelOp = 6;
  vtcFloat = 7;
  vtcDelimiter = 8;
  vtcEof = 9;

procedure FindErrorPos(InString: string; ErrPos: integer;
  var TheLine, TheCol: integer);
function GetWord(var InString: string; var StartPos: integer;
  var VarType: integer): string;
function GetWordByDelim(var InString: string; var StartPos: integer;
  var Delim: string): string;
function PeekWord(var InString: string; StartPos: integer;
  var VarType: integer): string;
function Recover(var InString: string; var StartPos: integer): boolean;
function StrToIntX(StrIn: string): integer;
function StrToDateX(TheDate: string): TDateTime;
function StrToDateTimeX(const S: string): TDateTime;
function YYYYMMDDParser(Str: PChar): TDateTime;

implementation

 //==============================================================================
 // Convert dates to a correct datetime notation. Try several notations,
 // starting with the system defaults                           
 //==============================================================================

function StrToDateTimeX(const S: string): TDateTime;
begin
  if S = '' then
     StrToDateTimeX := 0
  else
     StrToDateTimeX := StrToDateTime(S);
end;

function StrToDateX(TheDate: string): TDateTime;
var
  DateFormat: string;
  DateSep:    char;
begin
  DateFormat := ShortDateFormat; // save current settings
  DateSep    := DateSeparator;
  try
    try
      StrToDateX      := StrToDate(TheDate)
    except
      DateSeparator   := '-';
      ShortDateFormat := 'dd-mm-yyyy';
      try
        StrToDateX      := StrToDate(TheDate)
      except
        ShortDateFormat := 'yyyy-mm-dd';
        try
          StrToDateX := StrToDate(TheDate)
        except
          StrToDateX := StrToDateX('01-01-1900');
          raise;
        end;
      end;
    end;
  finally
    ShortDateFormat := DateFormat;
    DateSeparator   := DateSep;
  end;
end;

// Routine submitted by jpierce, modified to accept more types
// It requires that the date be in strict yyyy-mm-dd [hh:nn:[ss[:mmm]]]

function YYYYMMDDParser(Str: PChar): TDateTime;
var
  Year, Month, Day, Hour, Min, Sec, MSec: Word;
begin
  Result := 0;

  try
    if Length(Str) >= 10 then // 10 = Length of YYYY-MM-DD
    begin
      Year := StrToInt(Copy(Str, 1, 4));
      Month := StrToInt(Copy(Str, 6, 2));
      Day := StrToInt(Copy(Str, 9, 2));

      Result := EncodeDate(Year, Month, Day);
    end;

    if Length(Str) > 10 then // it has a time
    begin
      Hour := StrToInt(Copy(Str, 12, 2));
      Min := StrToInt(Copy(Str, 15, 2));
      Sec := 0;
      MSec := 0;
      if Length(Str) > 16 then Sec := StrToInt(Copy(Str, 18, 2));
      if Length(Str) > 19 then Msec := StrToInt(Copy(Str, 21, 3));
      Result := Result + EncodeTime(Hour, Min, Sec, MSec);
    end;
  except
    Result := 0;
  end; 
end; 

function StrToIntX(StrIn: string): integer;
begin
  try
    StrToIntX := StrToInt(StrIn);
  except
    StrToIntX := 0;
  end;
end;

procedure FindErrorPos(InString: string; ErrPos: integer;
  var TheLine, TheCol: integer);
var
  i: integer;
begin
  TheLine := 1;
  TheCol := 1;
  i := 1;
  while i < ErrPos do
  begin
    if InString[i] in [ #10, #13] then
    begin
      Inc(TheLine);
      TheCol := 1;
      Inc(i);
      Inc(i);
    end
    else
    begin
      Inc(TheCol);
      Inc(i);
    end;
  end;
end;

function Recover(var InString: string;
  var StartPos: integer): boolean;
begin
  if (StartPos > Length(InString)) then
  begin
    Recover := false;
    exit;
  end;

  while (Startpos < Length(InString)) and
    ( not (InString[StartPos] in [ #10, #13])) do
    Inc(StartPos);
  Recover := true;
end;

function PeekWord(var InString: string; StartPos: integer;
  var VarType: integer): string;
begin
  PeekWord := GetWord(InString, StartPos, VarType);
end;

function GetWordByDelim(var InString: string;
  var StartPos: integer;
  var Delim: string): string;
var
  Ret: string;
begin
  Ret := '';
  while (StartPos <= Length(InString)) and (InString[StartPos] = ' ') do
    Inc(StartPos);
  while (StartPos <= Length(InString)) and (Pos(InString[StartPos], Delim) = 0) do
  begin
    Ret := Ret + InString[StartPos];
    Inc(StartPos);
  end;
  GetWordByDelim := Trim(Ret);
end;

function GetWord(var InString: string; var StartPos: integer;
  var VarType: integer): string;
var
  TheChar: char;
  Rv:      string;
begin
  if (StartPos > Length(InString)) then
  begin
    GetWord := '';
    VarType := vtcEof;
    exit;
  end;

  while (StartPos <= Length(InString)) and (InString[StartPos] <= #32) do
    Inc(StartPos);

  TheChar := InString[StartPos];
  Rv      := '';

  if TheChar in ['a'..'z', 'A'..'Z'] then
    VarType := vtcIdentifier
  else if TheChar in ['0'..'9', '-'] then
    VarType := vtcNumber
  else if TheChar = ':' then
    VarType := vtcAssignment
  else if TheChar = '"' then
    VarType := vtcDString
  else if TheChar = '''' then
    VarType := vtcQString
  else if TheChar in ['>', '=', '<'] then
    VarType := vtcRelOp
  else
  begin
    Inc(StartPos);
    if TheChar = '!' then
    begin
      Recover(InString, StartPos);
      Rv      := GetWord(InString, StartPos, VarType);
      GetWord := Rv;
    end
    else
    begin
      GetWord := TheChar;
    end;
    exit;
  end;

  case VarType of
    vtcIdentifier:
    begin
      while InString[StartPos] in ['a'..'z', 'A'..'Z', '_'] do
      begin
        Rv := Rv + InString[StartPos];
        Inc(StartPos);
      end;
    end;
    vtcNumber:
    begin
      while InString[StartPos] in ['-', '0'..'9', '.'] do
      begin
        if InString[StartPos] = '.' then
          VarType := vtcFloat;
        Rv := Rv + InString[StartPos];
        Inc(StartPos);
      end;
      if VarType = vtcFloat then
        Rv := FloatToStr(StrToFloat(Rv))
      else
        Rv := IntToStr(StrToInt(Rv));
    end;
    vtcAssignment:
    begin
      Rv := InString[StartPos];
      Inc(StartPos);
      if InString[StartPos] = '=' then
      begin
        Inc(StartPos);
        Rv := ':=';
      end
      else
      begin
        VarType := vtcDelimiter;
        Rv      := ':';
      end;
    end;
    vtcQString:
    begin
      Inc(StartPos);
      while InString[StartPos] <> '''' do
      begin
        Rv := Rv + InString[StartPos];
        Inc(StartPos);
      end;
      Inc(StartPos);
    end;
    vtcDString:
    begin
      Inc(StartPos);
      while InString[StartPos] <> '"' do
      begin
        Rv := Rv + InString[StartPos];
        Inc(StartPos);
      end;
      Inc(StartPos);
    end;
    vtcRelOp:
    begin
      Rv := InString[StartPos];
      if Rv = '<' then
      begin
        if InString[StartPos + 1] in ['=', '>'] then
        begin
          Rv := Rv + InString[StartPos + 1];
          StartPos := StartPos + 2;
        end
        else
        begin
          Inc(StartPos);
        end;
      end
      else if Rv = '>' then
      begin
        if InString[StartPos + 1] in ['=', '<'] then
        begin
          Rv := Rv + InString[StartPos + 1];
          StartPos := StartPos + 2;
        end
        else
        begin
          Inc(StartPos);
        end;
      end
      else
      begin
        Inc(StartPos);
      end;
    end;
  end;
  GetWord := Rv;
end;

end.
