{$MODE FPC}
{$MODESWITCH DEFAULTPARAMETERS}
{$MODESWITCH Out}
{$MODESWITCH RESULT}
unit dparser_json;

interface

uses
  dparser;

const
  PARSER_JSON_ERROR_PLUS_NOT_ALLOWED                    = PARSER_ERROR_CLIENT + 0;
  PARSER_JSON_ERROR_FRACTIONAL_PART_MUST_CONTAIN_DIGITS = PARSER_ERROR_CLIENT + 1;
  PARSER_JSON_ERROR_EXPONENT_MUST_CONTAIN_DIGITS        = PARSER_ERROR_CLIENT + 2;

//
//  ParseJsonFloat64
//
//      Parses floating point value in json format:
//
//      -? [0-9]+ (\.[0-9]+)? ((e|E)(-|+)?[0-9]*)
//
//  Errors:
//
//      PARSER_JSON_ERROR_PLUS_NOT_ALLOWED
//
//          Following + is not allowed in classic json.
//
//      PARSER_JSON_ERROR_FRACTIONAL_PART_MUST_CONTAIN_DIGITS
//
//          After dot point there must be digits.
//
//      PARSER_JSON_ERROR_EXPONENT_MUST_CONTAIN_DIGITS
//
//          After e or E must be digits.
//
function ParseJsonFloat64(var S: PAnsiChar;
                          SEnd: PAnsiChar;
                          out F: Double;
                          Err: PLongInt = nil): Boolean;

implementation

function ParseJsonFloat64(var S: PAnsiChar;
                          SEnd: PAnsiChar;
                          out F: Double;
                          Err: PLongInt = nil): Boolean;
var
  Sign: Boolean;
  IntPart: UInt64;
  FracPart: UInt64;
  Exponent: Int32;
  Marker: PAnsiChar;
  FracDigits: LongInt;
begin
  Sign := SkipMinus(S, SEnd);
  if not ParseDecimalU64(S, SEnd, IntPart, Err) then begin
    if S^ = '+' then begin
      // 'plus is not allowed before numbers'
      Exit(FillParsingError(Err, PARSER_JSON_ERROR_PLUS_NOT_ALLOWED));
    end;
    Exit(False);
  end;
  if S >= SEnd then begin
    if Sign then begin
      F := - IntPart;
    end else
      F := IntPart;
    Exit(True);
  end;
  if (S < SEnd) and (S^ = '.') then begin
    Inc(S);
    Marker := S;
    if not ParseDecimalU64(S, SEnd, FracPart, Err) then
      Exit(FillParsingError(Err, PARSER_JSON_ERROR_FRACTIONAL_PART_MUST_CONTAIN_DIGITS));
    FracDigits := S - Marker;
  end else begin
    FracPart := 0;
    FracDigits := 0;
  end;
  if (S < SEnd) and (S^ in ['e', 'E']) then begin
    Inc(S);
    if not ParseDecimalI32(S, SEnd, Exponent, Err) then
      Exit(FillParsingError(Err, PARSER_JSON_ERROR_EXPONENT_MUST_CONTAIN_DIGITS));
  end else
    Exponent := 0;

  Dec(Exponent, FracDigits);
  while FracDigits >= 11  do begin
    IntPart := IntPart * POWEROF10[11];
    Dec(FracDigits, 11);
  end;
  if FracDigits > 0 then begin
    IntPart := POWEROF10[FracDigits] * IntPart + FracPart;
  end else
    Inc(IntPart, FracPart);


  Exit(BuildDoubleFromDecimalParts(Ord(Sign), IntPart, Exponent, F));
end;

end.
