#include "json-to-value.hh" #include <cstring> namespace nix { static void skipWhitespace(const char*& s) { while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r') { s++; } } static string parseJSONString(const char*& s) { string res; if (*s++ != '"') { throw JSONParseError("expected JSON string"); } while (*s != '"') { if (!*s) { throw JSONParseError("got end-of-string in JSON string"); } if (*s == '\\') { s++; if (*s == '"') { res += '"'; } else if (*s == '\\') { res += '\\'; } else if (*s == '/') { res += '/'; } else if (*s == '/') { res += '/'; } else if (*s == 'b') { res += '\b'; } else if (*s == 'f') { res += '\f'; } else if (*s == 'n') { res += '\n'; } else if (*s == 'r') { res += '\r'; } else if (*s == 't') { res += '\t'; } else if (*s == 'u') { throw JSONParseError( "\\u characters in JSON strings are currently not supported"); } else { throw JSONParseError("invalid escaped character in JSON string"); } s++; } else { res += *s++; } } s++; return res; } static void parseJSON(EvalState& state, const char*& s, Value& v) { skipWhitespace(s); if (!*s) { throw JSONParseError("expected JSON value"); } if (*s == '[') { s++; ValueVector values; values.reserve(128); skipWhitespace(s); while (1) { if (values.empty() && *s == ']') { break; } Value* v2 = state.allocValue(); parseJSON(state, s, *v2); values.push_back(v2); skipWhitespace(s); if (*s == ']') { break; } if (*s != ',') { throw JSONParseError("expected ',' or ']' after JSON array element"); } s++; } s++; state.mkList(v, values.size()); for (size_t n = 0; n < values.size(); ++n) { v.listElems()[n] = values[n]; } } else if (*s == '{') { s++; ValueMap attrs; while (1) { skipWhitespace(s); if (attrs.empty() && *s == '}') { break; } string name = parseJSONString(s); skipWhitespace(s); if (*s != ':') { throw JSONParseError("expected ':' in JSON object"); } s++; Value* v2 = state.allocValue(); parseJSON(state, s, *v2); attrs[state.symbols.create(name)] = v2; skipWhitespace(s); if (*s == '}') { break; } if (*s != ',') { throw JSONParseError("expected ',' or '}' after JSON member"); } s++; } state.mkAttrs(v, attrs.size()); for (auto& i : attrs) { v.attrs->push_back(Attr(i.first, i.second)); } v.attrs->sort(); s++; } else if (*s == '"') { mkString(v, parseJSONString(s)); } else if (isdigit(*s) || *s == '-' || *s == '.') { // Buffer into a string first, then use built-in C++ conversions std::string tmp_number; ValueType number_type = tInt; while (isdigit(*s) || *s == '-' || *s == '.' || *s == 'e' || *s == 'E') { if (*s == '.' || *s == 'e' || *s == 'E') { number_type = tFloat; } tmp_number += *s++; } try { if (number_type == tFloat) { mkFloat(v, stod(tmp_number)); } else { mkInt(v, stol(tmp_number)); } } catch (std::invalid_argument& e) { throw JSONParseError("invalid JSON number"); } catch (std::out_of_range& e) { throw JSONParseError("out-of-range JSON number"); } } else if (strncmp(s, "true", 4) == 0) { s += 4; mkBool(v, true); } else if (strncmp(s, "false", 5) == 0) { s += 5; mkBool(v, false); } else if (strncmp(s, "null", 4) == 0) { s += 4; mkNull(v); } else { throw JSONParseError("unrecognised JSON value"); } } void parseJSON(EvalState& state, const string& s_, Value& v) { const char* s = s_.c_str(); parseJSON(state, s, v); skipWhitespace(s); if (*s) { throw JSONParseError( format("expected end-of-string while parsing JSON value: %1%") % s); } } } // namespace nix