/// unit zaoberajuci sa zobrazovanim tabulky stavov pre LALR parser
unit LALRtable;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Grids, ComCtrls, ExtCtrls, constants, tables, hashmap, main;

type
  /// trieda zobrazujuca tabulku stavov LALR parsera
  TLALRTableForm = class(TForm)
    Panel1: TPanel;
    Panel2: TPanel;
    Panel3: TPanel;
    StatusBar1: TStatusBar;
    StringGrid1: TStringGrid;
    ScrollBar1: TScrollBar;
    StringGrid2: TStringGrid;
    Splitter1: TSplitter;
    Panel4: TPanel;
    Panel5: TPanel;
    Panel6: TPanel;
    procedure FormDestroy(Sender: TObject);
    procedure Panel6DblClick(Sender: TObject);
    procedure Panel4DblClick(Sender: TObject);
    procedure Panel4Click(Sender: TObject);
    procedure Panel6Click(Sender: TObject);
    procedure StringGrid2DblClick(Sender: TObject);
    procedure StringGrid1DblClick(Sender: TObject);
    procedure FormResize(Sender: TObject);
    procedure ScrollBar1Change(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    conflictArray: array of Integer;
    lastcol1,lastcol2: Integer;
  public
    procedure newparser(C: TIntIntHashSet);
    procedure showState(state: Integer);
    procedure fill(state: Integer; G: TSetOfItemSets; gototable,reducetable: TIntIntHashSet; T, N: TStrIntHashMap; R: TRuleTable);
    { Public declarations }
  end;

var
  LALRTableForm: TLALRTableForm;

implementation

{$R *.dfm}

uses grammaredit,langres, mainframe, folderdiff, tableframe, simple;

/// procedura, ktora naplni tabulku akcii a redukcii LALR parsera (pre vstupny stav state,
/// vstupnu mnozinu mnozin stavov G, goto a reduce tabulky - gotoTable a reduceTable,
/// a vstupne mena tokenov T,N a vstupnu tabulku pravidiel R)
procedure TLALRTableForm.fill(state: Integer; G: TSetOfItemSets; gototable,reducetable: TIntIntHashSet; T, N: TStrIntHashMap; R: TRuleTable);
var index,i,k: Integer;
    S,Sstate: xString;
    value,posit: Integer;
    regexp: xString;
    b: Boolean;
    itemset: TItemSet;
    hashset: TIntHashSet;
    GGHashSet,IIHashSet: TIntIntHashSet;
    list: TStringList;
    j,symbol,pos: Integer;
    rr,sr: Integer;
begin
  if (state > Pred(G.getSize)) or (state < 0) then begin
    Exit;
  end;

  with StringGrid1 do begin
    cells[0,0] := GetLangString(STR_RULENUMBER);
    cells[1,0] := GetLangString(STR_LR0ITEM);
    cells[2,0] := GetLangString(STR_LOOKAHEAD);
//    cells[3,0] := GetLangString(STR_REGEXP);
  end;

  itemset := G.a[state];

  list := itemset.toStringList(true,T,N,R);

  StringGrid1.RowCount := 1 + list.Count shr 1;
  for i := 0 to Pred(list.Count shr 1) do begin
    with StringGrid1 do begin
      cells[0,i+1] := IntToStr(Integer(list.Objects[i shl 1]));
      cells[1,i+1] := list.Strings[i shl 1 + 1];
      cells[2,i+1] := list.Strings[i shl 1];
    end;
  end;

  list.Free;

  with StringGrid2 do begin
    cells[0,0] := GetLangString(STR_ACTIONTYPE);
    cells[1,0] := GetLangString(STR_TOKEN);
    cells[2,0] := GetLangString(STR_ACTION);
//    cells[3,0] := GetLangString(STR_REGEXP);
  end;


//  S := '';
//  writeln(gotoTable.toString(true));
//  S := S + '###gotoTable###' + EOL;
  rr := 0;
  sr := 0;

  k := 1;
  i := 1;
  Sstate := GetLangString(STR_SHIFT);

//  gotoTable.getNextReset;
  if gotoTable.get(state,Integer(GGhashset)) then begin
    k := k + GGhashset.getSize;
    if k > 1 then
      StringGrid2.RowCount := k;
    GGhashset.getNextReset;
    while GGhashset.getNext(symbol,J) do begin
      with StringGrid2 do begin
        cells[0,i] := Sstate;
        if symbol <= MAX_TERMINAL then begin
          if not T.getByValue(symbol,S,pos) then
            S := '?' + IntToStr(symbol) + '?';
        end
        else begin
          if not N.getByValue(symbol,S,pos) then
            S := '?<' + IntToStr(symbol) + '>?'
          else
            S := '<' + S + '>';
        end;
        cells[1,i] := S;
        cells[2,i] := IntToStr(J);
      end;
      i := i + 1;
//      S := S + '[itemset ' + IntToStr(I) + '] on ' + IntToStr(symbol) + ' (' + IntToStr(Integer(IIhashset)) + ') shifts to ' + IntToStr(J);
//      S := S + EOL;
    end;
  end;

  Sstate := GetLangString(STR_REDUCE);
//  writeln(FreduceTable.toString(true));
//  S := S + '###reduceTable###' + EOL;
//  reduceTable.getNextReset;
  if reduceTable.get(state,Integer(IIhashset)) then begin
//    writeln(IntToStr(I) + ' - ' + IntToStr(Integer(IIhashset)) + EOL + IIhashset.toString(true));
    k := k + IIhashset.getSize;
    if k > 1 then
      StringGrid2.RowCount := k;
    IIhashset.getNextReset;
    while IIhashset.getNext(symbol,Integer(hashset)) do begin
      if hashset.getSize > 1 then begin
        rr := rr + hashset.getSize - 1;
        StringGrid2.RowCount := StringGrid2.RowCount + hashset.getSize - 1;
      end;
      if GGhashset <> nil then
        if GGhashset.get(symbol,J) then
          sr := sr + 1;

      hashset.getNextReset;
      while hashset.getNext(J) do begin
        with StringGrid2 do begin
          cells[0,i] := Sstate;
          if symbol <= MAX_TERMINAL then begin
            if not T.getByValue(symbol,S,pos) then
              S := '?' + IntToStr(symbol) + '?';
          end
          else begin
            if not N.getByValue(symbol,S,pos) then
              S := '?<' + IntToStr(symbol) + '>?'
            else
              S := '<' + S + '>';
          end;
          cells[1,i] := S;
          cells[2,i] := IntToStr(J);
        end;
        i := i + 1;
      end;
//      S := S + '[itemset ' + IntToStr(I) + '] on ' + IntToStr(J) + ' (' + IntToStr(Integer(hashset)) + ') reduces by rules ' + hashset.toString(false);
//      S := S + EOL;
    end;
  end;

  S := '';
  if (sr > 0) or (rr > 0) then begin
    S := ' (shift/reduce conflicts: ' + IntToStr(sr) + ' , reduce/reduce conflicts: ' + IntToStr(rr) + ')';
  end;
  Panel5.Caption := '[LALR State: ' + IntToStr(state) + ' / ' + IntToStr(Pred(G.getSize)) + ' ]' + S;
  Panel5.Hint := Panel5.Caption;

  StatusBar1.Panels[0].Text := 'Number Of States: ' + IntToStr(G.getSize);

  //sort them

  if lastcol1 <> -1 then begin
    i := lastcol1 shr 1;
    if lastcol1 mod 2 = 0 then
      StringGrid1.Tag := -1
    else
      StringGrid1.Tag := lastcol1 shr 1;
    if (i = 0) then begin
      GridSort(StringGrid1,i,1,StringGrid1.RowCount-1,celltypeInteger);
    end
    else begin
      GridSort(StringGrid1,i,1,StringGrid1.RowCount-1,celltypeString);
    end;
    if lastcol1 mod 2 = 0 then
      StringGrid1.Tag := lastcol1 shr 1
    else
      StringGrid1.Tag := -1;
//    StringGrid1.Tag := i;
  end;

  if lastcol2 <> -1 then begin
    i := lastcol2 shr 1;
    if lastcol2 mod 2 = 0 then
      StringGrid2.Tag := -1
    else
      StringGrid2.Tag := lastcol2 shr 1;
    if (i = 2) then begin
      GridSort(StringGrid2,i,1,StringGrid2.RowCount-1,celltypeInteger);
    end
    else begin
      GridSort(StringGrid2,i,1,StringGrid2.RowCount-1,celltypeString);
    end;
    if lastcol2 mod 2 = 0 then
      StringGrid2.Tag := lastcol2 shr 1
    else
      StringGrid2.Tag := -1;
//    StringGrid2.Tag := i;
  end;
end;

/// procedura, ktora vytvori okno zobrazujuce tabulku akcii a redukcii LALR parsera (pre vstupny stav state)
procedure TLALRTableForm.showState(state: Integer);
begin
  if GrammarEditForm.FParser <> nil then begin
    StringGrid1.Enabled := true;
    StringGrid2.Enabled := true;
    with GrammarEditForm.FParser do begin
      fill(state,FglobalSet,FgotoTable,FReduceTable,LTokenNameTypeTable,LStateNameTypeTable,LRuleTable);
    end;
  end
  else begin
    StringGrid1.Enabled := false;
    StringGrid2.Enabled := false;
    ScrollBar1.Min := 0;
    ScrollBar1.Max := 0;
    StringGrid1.Tag := -1;
    StringGrid2.Tag := -1;
    lastcol1 := -1;
    lastcol2 := -1;
    Panel5.Caption := '';
    StatusBar1.Panels[0].Text := '';
  end;
end;

/// procedura, ktora vytvori okno zobrazjuce tabulku stavov LALR parsera
procedure TLALRTableForm.FormCreate(Sender: TObject);
begin
  StringGrid1.Enabled := false;
  StringGrid1.DoubleBuffered := true;
  StringGrid2.Enabled := false;
  StringGrid2.DoubleBuffered := true;
  Panel4.DoubleBuffered := true;
  Panel5.DoubleBuffered := true;
  Panel6.DoubleBuffered := true;
  ScrollBar1.Min := 0;
  ScrollBar1.Max := 0;
  StringGrid1.Tag := -1;
  StringGrid2.Tag := -1;
  lastcol1 := -1;
  lastcol2 := -1;
end;

/// procedura na obsluhu posunu scrollbaru, pri zmene sa zmeni zobrazovany stav na momentalnu poziciu scrollbaru
procedure TLALRTableForm.ScrollBar1Change(Sender: TObject);
begin
  showState(ScrollBar1.Position);
end;

/// procedura na reinicializaciu tabulky stavov LALR parsera
procedure TLALRTableForm.newparser(C: TIntIntHashSet);
var i,j: Integer;
begin
  if GrammarEditForm.FParser <> nil then begin
    ScrollBar1.Min := 0;
    ScrollBar1.Max := Pred(GrammarEditForm.FParser.FglobalSet.getSize);

    if conflictArray <> nil then begin
      conflictArray := nil;
    end;

    if conflictArray = nil then begin
      if C <> nil then begin
        SetLength(conflictArray,C.getSize);
        C.getNextReset;
        while C.getNext(j,i) do begin
          conflictArray[i] := j;
        end;
      end;
    end;

    showState(0);
  end;
end;

/// procedura, ktora automaticky meni velkost stlpcov pri zmene velkosti okna
procedure TLALRTableForm.FormResize(Sender: TObject);
var i: Integer;
begin
  for i := 0 to Pred(StringGrid1.ColCount) do
    StringGrid1.ColWidths[i] := StringGrid1.ClientWidth div StringGrid1.ColCount - 1;
  for i := 0 to Pred(StringGrid2.ColCount) do
    StringGrid2.ColWidths[i] := StringGrid2.ClientWidth div StringGrid2.ColCount - 1;
end;

/// procedura na obsluhu dvojkliku vo vnutri vrchnej casti tabulky
procedure TLALRTableForm.StringGrid1DblClick(Sender: TObject);
var SG,SG2: TStringGrid;
    S: xString;
    s2: xString;
    sname,s2name: xString;
    mpos: TPoint;
    i,X,Y: Integer;
    ACol,ARow: Integer;
    TGR: TGridRect;
begin
  SG := StringGrid1;

  mpos := Mouse.CursorPos;
  X := SG.ScreenToClient(mpos).X;
  Y := SG.ScreenToClient(mpos).Y;
  SG.MouseToCell(X, Y, ACol, ARow);

  if ARow = 0 then begin
    if (ACol = 0) then begin
      GridSort(SG,ACol,1,SG.RowCount-1,celltypeInteger);
    end
    else begin
      GridSort(SG,ACol,1,SG.RowCount-1,celltypeString);
    end;
    if SG.Tag <> -1 then
      lastcol1 := ACol shl 1
    else
      lastcol1 := ACol shl 1 + 1;
    Exit;
  end
  else if (ARow > 0) and (ARow < SG.RowCount) then begin
    S := StringGrid1.Cells[0,StringGrid1.Row];
    for i := 0 to Pred(RuleTableForm.StringGrid1.RowCount) do begin
      if RuleTableForm.StringGrid1.Cells[0,i] = S then begin
        RuleTableForm.StringGrid1.Row := i;
        MainForm.ShowRuleTableTBClick(Sender);
        break;
      end;
    end;
  end;
end;

/// procedura na obsluhu dvojkliku vo vnutri spodnej casti tabulky
procedure TLALRTableForm.StringGrid2DblClick(Sender: TObject);
var SG,SG2: TStringGrid;
    S: xString;
    s2: xString;
    sname,s2name: xString;
    mpos: TPoint;
    i,X,Y: Integer;
    ACol,ARow: Integer;
    TGR: TGridRect;
begin
  SG := StringGrid2;

  mpos := Mouse.CursorPos;
  X := SG.ScreenToClient(mpos).X;
  Y := SG.ScreenToClient(mpos).Y;
  SG.MouseToCell(X, Y, ACol, ARow);

  if ARow = 0 then begin
    if (ACol = 2) then begin
      GridSort(SG,ACol,1,SG.RowCount-1,celltypeInteger);
    end
    else begin
      GridSort(SG,ACol,1,SG.RowCount-1,celltypeString);
    end;
    if SG.Tag <> -1 then
      lastcol2 := ACol shl 1
    else
      lastcol2 := ACol shl 1 + 1;
    Exit;
  end
  else if (ARow > 0) and (ARow < SG.RowCount) then begin
    if ACol = 2 then begin
      if StringGrid2.Cells[0,StringGrid2.Row] = GetLangString(STR_SHIFT) then begin
        i := StrToIntDef(StringGrid2.Cells[2,ARow],-1);
        if (i >= ScrollBar1.Min) and (i <= ScrollBar1.Max) then
          ScrollBar1.Position := i;
      end
      else begin
        S := StringGrid2.Cells[2,ARow];
        for i := 0 to Pred(RuleTableForm.StringGrid1.RowCount) do begin
          if RuleTableForm.StringGrid1.Cells[0,i] = S then begin
            RuleTableForm.StringGrid1.Row := i;
            MainForm.ShowRuleTableTBClick(Sender);
            break;
          end;
        end;
      end;
    end
    else if ACol = 1 then begin
      S := StringGrid2.Cells[1,StringGrid2.Row];
      for i := 0 to Pred(TokenTableForm.StringGrid1.RowCount) do begin
        if TokenTableForm.StringGrid1.Cells[1,i] = S then begin
          TokenTableForm.StringGrid1.Row := i;
          MainForm.ShowTokenTableTBClick(Sender);
          break;
        end;
      end;
    end;
  end;
end;

/// procedura volana pri kliknuti na tlacitko '>' v pravom hornom rohu,
/// posuvajuca zobrazenie na stav v ktorom je najblizsie
/// nejaky konflikt v smere rastucich indexov stavov
procedure TLALRTableForm.Panel6Click(Sender: TObject);
var i,j,t,state: Integer;
    lastindex: Integer;
    res: Integer;
begin
  if conflictArray = nil then
    Exit;

  state := ScrollBar1.Position;
//  i := (ScrollBar1.Max - ScrollBar1.Min) shr 1;
  lastindex := Pred(Length(conflictArray));

  if conflictArray[0] > state then
    res := 0
  else if conflictArray[lastindex] < state then
    res := 0
  else begin
    i := 0;
    j := lastindex;
    while i < j do begin
      t := (i+j) shr 1;
      if conflictArray[t] > state then
        j := t
      else
        i := t + 1;
    end;
    res := i;
  end;

  if res = 0 then
    ScrollBar1.Position := conflictArray[res]
  else if conflictArray[res] > state then
    ScrollBar1.Position := conflictArray[res]
  else if conflictArray[res] <= state then
    ScrollBar1.Position := conflictArray[overflowwrap(res+1,lastindex+1)];
end;

/// procedura volana pri kliknuti na tlacitko '<' v lavom hornom rohu,
/// posuvajuca zobrazenie na stav v ktorom je najblizsie
/// nejaky konflikt v smere klesajucich indexov stavov
procedure TLALRTableForm.Panel4Click(Sender: TObject);
var i,j,t,state: Integer;
    lastindex: Integer;
    res: Integer;
begin
  if conflictArray = nil then
    Exit;

  state := ScrollBar1.Position;
//  i := (ScrollBar1.Max - ScrollBar1.Min) shr 1;
  lastindex := Pred(Length(conflictArray));

  if conflictArray[0] > state then begin
    res := lastindex;
    ScrollBar1.Position := conflictArray[res]
  end
  else if conflictArray[lastindex] < state then begin
    res := lastindex;
    ScrollBar1.Position := conflictArray[res]
  end
  else begin
    i := 0;
    j := lastindex;
    while i < j do begin
      t := (i+j) shr 1;
      if conflictArray[t] >= state then
        j := t
      else
        i := t + 1;
    end;
    res := i;
    if conflictArray[res] < state then
      ScrollBar1.Position := conflictArray[res]
    else if conflictArray[res] >= state then
      ScrollBar1.Position := conflictArray[zerowrap(res-1,lastindex+1)];
  end;
end;

/// procedura volana pri dvojkliku na tlacitko '<' v lavom hornom rohu, zavola dva krat
/// proceduru na obsluhu obycajneho kliknutia na tlacitko '<'
procedure TLALRTableForm.Panel4DblClick(Sender: TObject);
begin
  Panel4Click(Sender);
  Panel4Click(Sender);
end;

/// procedura volana pri dvojkliku na tlacitko '>' v pravom hornom rohu, zavola dva krat
/// proceduru na obsluhu obycajneho kliknutia na tlacitko '>'
procedure TLALRTableForm.Panel6DblClick(Sender: TObject);
begin
  Panel6Click(Sender);
  Panel6Click(Sender);
end;

/// procedura volana tesne pred odstranenim objektu z pamate
procedure TLALRTableForm.FormDestroy(Sender: TObject);
begin
  conflictArray := nil;
end;

end.
