program demo_expression_parser;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}{$IFDEF UseCThreads}
  cthreads,
  {$ENDIF}{$ENDIF}
  SysUtils, Classes
  { you can add units after this },
  TypInfo, fpsTypes, fpSpreadsheet, fpsUtils, fpsExprParser;

var
  workbook: TsWorkbook;
  worksheet: TsWorksheet;
  cell: PCell;
  parser: TsExpressionParser;
  res: TsExpressionResult;
  formula: TsRPNFormula;
  i: Integer;
  s: String;

begin
  workbook := TsWorkbook.Create;
  try
    worksheet := workbook.AddWorksheet('Test');

    worksheet.WriteNumber(0, 0, 1);                  // A1
    worksheet.WriteNumber(0, 1, 2.5);                // B1
    {
    worksheet.WriteText(0, 0, 'Hallo');              // A1
    worksheet.WriteText(0, 1, 'World');              // B1
    }

    { Select one of these formulas }
    //cell := worksheet.WriteFormula(1, 0, '=4+5');    // A2
    //cell := worksheet.WriteFormula(1, 0, 'AND(TRUE(), TRUE(), TRUE())');
    cell := worksheet.WriteFormula(1, 0, 'SIN(A1+B1)');
    //cell := worksheet.WriteFormula(1, 0, '=TRUE()');
    //cell := worksheet.WriteFormula(1, 0, '=1-(4/2)^2*2-1');    // A2
    //cell := Worksheet.WriteFormula(1, 0, 'datedif(today(),Date(2014,1,1),"D")');
    //cell := Worksheet.WriteFormula(1, 0, 'Day(Date(2014, 1, 12))');
    //cell := Worksheet.WriteFormula(1, 0, 'SUM(1,2,3)');
    //cell := Worksheet.WriteFormula(1, 0, 'CELL("address",A1)');
    //cell := Worksheet.WriteFormula(1, 0, 'REPT("Hallo", 3)');
    //cell := Worksheet.WriteFormula(1, 0, '#REF!');

    WriteLn('A1: ', worksheet.ReadAsText(0, 0));
    WriteLn('B1: ', worksheet.ReadAsText(0, 1));

    parser := TsSpreadsheetParser.Create(worksheet);
    try
      try
        parser.Expression[fdExcelA1] := worksheet.ReadFormula(cell);
        res := parser.Evaluate;

        WriteLn('A2: ', parser.Expression[fdExcelA1]);
        Write('Result: ');
        case res.ResultType of
          rtEmpty    : WriteLn('--- empty ---');
          rtBoolean  : WriteLn(BoolToStr(res.ResBoolean, true));
          rtFloat    : WriteLn(FloatToStr(res.ResFloat));
          rtInteger  : WriteLn(IntToStr(res.ResInteger));
          rtDateTime : WriteLn(FormatDateTime('c', res.ResDateTime));
          rtString   : WriteLn(res.ResString);
          rtError    : WriteLn(GetErrorValueStr(res.ResError));
        end;

        WriteLn('Reconstructed string formula: ', parser.Expression[fdExcelA1]);
        WriteLn('Reconstructed localized formula: ', parser.Expression[fdLocalized]);
        formula := parser.RPNFormula;

        for i:=0 to Length(formula)-1 do begin
          Write('  Item ', i, ': token ', GetEnumName(TypeInfo(TFEKind), ord(formula[i].ElementKind)), ' ', formula[i].FuncName);
          case formula[i].ElementKind of
            fekCell    : Write(' / cell: ' +GetCellString(formula[i].Row, formula[i].Col, formula[i].RelFlags));
            fekNum     : Write(' / float value: ', FloatToStr(formula[i].DoubleValue));
            fekInteger : Write(' / integer value: ', IntToStr(formula[i].IntValue));
            fekString  : Write(' / string value: "', formula[i].StringValue, '"');
            fekBool    : Write(' / boolean value: ', BoolToStr(formula[i].DoubleValue <> 0, true));
            fekErr     : Write(' / error value: ', GetErrorValueStr(TsErrorValue(formula[i].IntValue)));
          end;
          WriteLn;
        end;
      finally
        parser.Free;
      end;

    except on E:Exception do
      begin
        WriteLn('Parser/calculation error: ', E.Message);
        raise;
      end;
    end;

    parser := TsSpreadsheetParser.Create(worksheet);
    try
      try
        parser.RPNFormula := formula;
        s := parser.Expression[fdExcelA1];
        WriteLn('String formula, reconstructed from RPN formula: ', s);
      except on E:Exception do
        begin
          WriteLn('RPN/string formula conversion error: ', E.Message);
          raise;
        end;
      end;
    finally
      parser.Free;
    end;

  finally
    workbook.Free;
  end;

  if ParamCount = 0 then
  begin
    {$IFDEF MSWINDOWS}
    WriteLn('Press [ENTER] to quit...');
    ReadLn;
    {$ENDIF}
  end;

end.