#line 1378 "/home/travis/build/felix-lang/felix/src/packages/web.fdoc"
  open class Json
  {
    union Jvalue =
    | Jstring of string
    | Jnumber of string
    | Jdictionary of list[Jpair]
    | Jarray of list [Jvalue]
    | Jname of string
    ;
    typedef Jpair = Jvalue * Jvalue;
  
    fun str (s:Jvalue, v:Jvalue) : string => str s + ': ' + str v;
  
    fun str (v: Jvalue) : string => match v with
    | Jstring s => '"' + s + '"' // hack, ignores quoting rules
    | Jnumber i => i
    | Jdictionary d => "{" + cat ", " (map str of (Jpair) d) + "}"
    | Jarray a => "[" + cat ", " (map str of (Jvalue) a) + "]"
    | Jname a => a
    endmatch
    ;
  
    union ParseResult =
    | Good of Jvalue
    | Bad of int
    ;
  
    fun parse_json(s:string): ParseResult = {
      var i = skip_white s 0;
      def i, var v = parse_value s i;
      i = skip_white s i;
      if s.[i] != "".char do
        return Bad i;
      else
        return v;
      done
    }
  
    private fun skip_white (s:string) (var i:int) = {
      while s.[i] in " \t\r\n" do ++i; done
      return i;
    }
  
    private fun parse_value (s:string) (i:int): int * ParseResult =>
      if s.[i] in "-0123456789" then parse_number s i
      elif s.[i] == '"'.char then parse_string s (i+1)
      elif s.[i] == "{".char then parse_dictionary s (i+1)
      elif s.[i] ==  "[".char then parse_array s (i+1)
      elif s.[i] in "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" then parse_name s i
      else i, Bad i
      endif
    ;
  
    private fun parse_name (s:string) (var i:int) = {
      var j = s.[i].string;
      ++i;
      while s.[i] in "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_" do
         j += s.[i];
         ++i;
      done
      if j in ("true","false","null") do
        return i,Good (Jname j);
      else
        return i, Bad i;
      done
    }
  
    private fun parse_number (s:string) (var i:int) = {
      var j = "";
  
      // optional leading sign
      if s.[i] == "-".char do
        j += s.[i];
        ++i;
      done
  
      // zero integral part
      if s.[i] == "0".char do
        j+= s.[i];
        ++i;
        goto point;
      done
  
      // nonzero integral part
      if s.[i] in "123456789" do
        j += s.[i];
        ++i;
      else
        goto bad;
      done
  
      // rest of integral part
      while s.[i] in "0123456789" do
         j += s.[i];
         ++i;
      done
  
  point:>
      if s.[i] != ".".char goto exponent;
      j += s.[i];
      ++i;
  
  fraction:>
      if s.[i] in "0123456789" do
        while s.[i] in "0123456789" do
           j += s.[i];
           ++i;
        done
      else
        goto bad;
      done
  
  exponent:>
      if s.[i] in "eE" do
        j += s.[i];
        ++i;
      else
        goto good;
      done
  
      // sign of exponent
      if s.[i] in "+-" do
        j += s.[i];
        ++i;
      done
  
      // exponent value
      if s.[i] in "0123456789" do
        while s.[i] in "0123456789" do
        j += s.[i];
        ++i;
        done
      else
        goto bad;
      done
  good:>
      return i,Good (Jnumber j);
  bad:>
      return i, Bad i;
    }
  
    private fun parse_string (s:string) (var i:int) = {
      var r = "";
  ordinary:>
      while s.[i] != "".char and s.[i] != '"'.char and s.[i] != "\\".char do
        if s.[i].ord < 32 goto bad; // control chars are not allowed
        r += s.[i];
        ++i;
      done
  
      if s.[i] == '"'.char do // closing quote
        ++i;
        goto good;
      elif s.[i] == "\\".char do // escape
        r += s.[i];
        ++i;
        if s.[i] in '"\\/bfnrt' do // one char escape code
          r += s.[i];
          ++i;
          goto ordinary;
        elif s.[i] == "u".char do // hex escape
          r += s.[i];
          ++i;
          if s.[i] in "0123456789ABCDEFabcdef" do r += s.[i]; ++i; else goto bad; done
          if s.[i] in "0123456789ABCDEFabcdef" do r += s.[i]; ++i; else goto bad; done
          if s.[i] in "0123456789ABCDEFabcdef" do r += s.[i]; ++i; else goto bad; done
          if s.[i] in "0123456789ABCDEFabcdef" do r += s.[i]; ++i; else goto bad; done
          goto ordinary;
        else
          goto bad;
        done
      else // end of input
        goto bad;
      done
  
  good:>
      return i,Good (Jstring r);
  bad:>
      return i, Bad i;
  }
  
    private fun parse_dictionary (s:string) (var i:int) = {
      var elts = #list[Jvalue * Jvalue];
      i = skip_white s i;
      while s.[i] != "}".char do
        if s.[i] == '"'.char do
          def i, var ms = parse_string s (i+1);
          match ms with
          | Good sv =>
            i = skip_white s i;
            if s.[i] == ":".char do
              ++i;
              i = skip_white s i;
              def i, var mv = parse_value s i;
              match mv with
              | Good v =>
                elts += sv,v;
                i = skip_white s i;
              | Bad j => return i, Bad j;
              endmatch;
            else
              return i, Bad i;
            done
            if s.[i] == ",".char do
              ++i;
              i = skip_white s i;
            elif s.[i] == "}".char do ;
            else
              return i, Bad i;
            done
          | Bad j => return i, Bad j;
          endmatch;
        else
          return i, Bad i;
        done
      done
      ++i;
      i = skip_white s i;
      return i, Good (Jdictionary elts);
    }
  
    private fun parse_array (s:string) (var i:int) = {
      var elts = #list[Jvalue];
      i = skip_white s i;
      while s.[i] != "]".char do
        def i, var mv = parse_value s i;
        match mv with
        | Good v => elts += v;
          i = skip_white s i;
          if s.[i] == ",".char do
            ++i;
            i = skip_white s i;
          elif s.[i] == "]".char do ;
          else
            return i, Bad i;
          done
        | Bad j => return i, Bad j;
        endmatch;
      done
      ++i;
      i = skip_white s i;
      return i, Good (Jarray elts);
    }
  }