(*
    History++ plugin for Miranda IM: the free IM client for Microsoft* Windows*

    Copyright (C) 2006-2009 theMIROn, 2003-2006 Art Fedorov.
    History+ parts (C) 2001 Christian Kastner

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    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.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*)

{-----------------------------------------------------------------------------
 hpp_strparser.pas (historypp project)

 Version:   1.5
 Created:   18.04.2006
 Author:    Oxygen

 [ Description ]

 This unit provides AnsiString parsing routines. Mainly it was added to
 parse tokens from the AnsiString. See TokenizeString for description.

 [ Modifications ]
 none

 [ Known Issues ]
 none

 Contributors: Art Fedorov
-----------------------------------------------------------------------------}

unit hpp_strparser;

interface

uses
  hpp_global;

procedure TokenizeString(const Template: String; var Tokens: TWideStrArray; var SpecialTokens: TIntArray);

implementation

{
  This procedure splits AnsiString into array.

  The split is based on three token types:
  1) general text
  2) chars prefixed with '\', like '\n'
  3) AnsiString surrounded by %'s, like '%mymom%'

  You input the AnsiString in Template variable and it outputs
  * Tokens: array of all tokens
  * SpecialTokens: array of token indexes from the Tokens array,
    where indexes are of 2nd and 3rd type tokens

  You can get the orginial template AnsiString if you combine all strings
  from tokens array. It means that Template = Tokens[0]+Tokens[1]+...+Tokens[n]

  The idea is that after recieving special tokens array, you can scan through
  them and change all the special tokens you want in the tokens array and then
  combine tokens array to get template with the needed tokens substituted

  *** Examples (special tokens in double quotes here):
     'My %mom% is good\not bad' -> 'My '+"%mom%"+' is good'+"\n"+'ot bad'
     '%My mom% is good' -> "%My mom%"+' is good'
  *** Placing \'s inside %'s would give you type 2 token, not type 3:
     '%My \mom% is good' -> '%My '+"\m"+'om% is good'
  *** \'s and %'s at the end of the line don't get counted:
     'My mom\' -> 'My mom\'
     'My mom%' -> 'My mom%'
     'My mom is %good' -> 'My mom is %good'
  *** But
     'My mom is %good%' -> 'My mom is '+"%good%"
  *** Double %'s is also counted as token:
     'My %% mom' -> 'My '+"%%"+' mom'

  So, feeding it 'My %mom% is good\nNot bad' would output:
  Tokens =>
    [0] -> 'My '
    [1] -> '%mom%'
    [2] -> ' is good'
    [3] -> '\n'
    [4] -> 'Not bad'
  SpecialTokens =>
    [0] -> 1
    [1] -> 3
}
procedure TokenizeString(const Template: String; var Tokens: TWideStrArray; var SpecialTokens: TIntArray);
var
  i,len: Integer;
  token_s: Integer;
  in_token: Boolean;

  procedure PushToken(StartIdx,EndIdx: Integer; Special: Boolean = False);
  begin
    if EndIdx < StartIdx then
      exit;
    if not Special then
    begin // if not special, try to append current token to previous
      if Length(Tokens) > 0 then
      begin
        if not ((Length(SpecialTokens) > 0) and
        (SpecialTokens[High(SpecialTokens)] = High(Tokens))) then  // previous was not special
        begin
          Tokens[High(Tokens)] := Tokens[High(Tokens)] + Copy(Template,StartIdx,EndIdx-StartIdx+1);
          exit;
        end;
      end;
    end;
    SetLength(Tokens,Length(Tokens)+1);
    Tokens[High(Tokens)] := Copy(Template,StartIdx,EndIdx-StartIdx+1);
    if Special then
    begin
      SetLength(SpecialTokens,Length(SpecialTokens)+1);
      SpecialTokens[High(SpecialTokens)] := High(Tokens);
    end;
  end;

begin
  len := Length(Template);
  SetLength(Tokens,0);
  SetLength(SpecialTokens,0);

  token_s := 1;
  in_token := False;
  i := 1;
  while i <= len do
  begin
    if (Template[i]='\') or (Template[i]='%') then
    begin
      if Template[i] = '\' then
      begin
        if i = len then
          break;
        PushToken(token_s,i-1);
        token_s := i;
        PushToken(token_s,token_s+1,True);
        token_s := i+2;
        i := token_s;
        in_token := False;
        continue;
      end
      else
      begin
        if in_token then
        begin
          PushToken(token_s,i,True);
          token_s := i + 1;
          in_token := False;
        end
        else
        begin
          PushToken(token_s,i-1);
          token_s := i;
          in_token := True;
        end;
      end;
    end;
    Inc(i);
  end;

  PushToken(token_s,len);
end;

end.