// Copyright 2019 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/torque/ls/json-parser.h" #include <cctype> #include "src/torque/earley-parser.h" namespace v8 { namespace internal { namespace torque { template <> V8_EXPORT_PRIVATE const ParseResultTypeId ParseResultHolder<ls::JsonValue>::id = ParseResultTypeId::kJsonValue; template <> V8_EXPORT_PRIVATE const ParseResultTypeId ParseResultHolder<std::pair<std::string, ls::JsonValue>>::id = ParseResultTypeId::kJsonMember; template <> V8_EXPORT_PRIVATE const ParseResultTypeId ParseResultHolder<std::vector<ls::JsonValue>>::id = ParseResultTypeId::kStdVectorOfJsonValue; template <> V8_EXPORT_PRIVATE const ParseResultTypeId ParseResultHolder<std::vector<std::pair<std::string, ls::JsonValue>>>::id = ParseResultTypeId::kStdVectorOfJsonMember; namespace ls { using JsonMember = std::pair<std::string, JsonValue>; template <bool value> base::Optional<ParseResult> MakeBoolLiteral( ParseResultIterator* child_results) { return ParseResult{JsonValue::From(value)}; } base::Optional<ParseResult> MakeNullLiteral( ParseResultIterator* child_results) { JsonValue result; result.tag = JsonValue::IS_NULL; return ParseResult{std::move(result)}; } base::Optional<ParseResult> MakeNumberLiteral( ParseResultIterator* child_results) { auto number = child_results->NextAs<std::string>(); double d = std::stod(number.c_str()); return ParseResult{JsonValue::From(d)}; } base::Optional<ParseResult> MakeStringLiteral( ParseResultIterator* child_results) { std::string literal = child_results->NextAs<std::string>(); return ParseResult{JsonValue::From(StringLiteralUnquote(literal))}; } base::Optional<ParseResult> MakeArray(ParseResultIterator* child_results) { JsonArray array = child_results->NextAs<JsonArray>(); return ParseResult{JsonValue::From(std::move(array))}; } base::Optional<ParseResult> MakeMember(ParseResultIterator* child_results) { JsonMember result; std::string key = child_results->NextAs<std::string>(); result.first = StringLiteralUnquote(key); result.second = child_results->NextAs<JsonValue>(); return ParseResult{std::move(result)}; } base::Optional<ParseResult> MakeObject(ParseResultIterator* child_results) { using MemberList = std::vector<JsonMember>; MemberList members = child_results->NextAs<MemberList>(); JsonObject object; for (auto& member : members) object.insert(std::move(member)); return ParseResult{JsonValue::From(std::move(object))}; } class JsonGrammar : public Grammar { static bool MatchWhitespace(InputPosition* pos) { while (MatchChar(std::isspace, pos)) { } return true; } static bool MatchStringLiteral(InputPosition* pos) { InputPosition current = *pos; if (MatchString("\"", ¤t)) { while ( (MatchString("\\", ¤t) && MatchAnyChar(¤t)) || MatchChar([](char c) { return c != '"' && c != '\n'; }, ¤t)) { } if (MatchString("\"", ¤t)) { *pos = current; return true; } } current = *pos; if (MatchString("'", ¤t)) { while ( (MatchString("\\", ¤t) && MatchAnyChar(¤t)) || MatchChar([](char c) { return c != '\'' && c != '\n'; }, ¤t)) { } if (MatchString("'", ¤t)) { *pos = current; return true; } } return false; } static bool MatchHexLiteral(InputPosition* pos) { InputPosition current = *pos; MatchString("-", ¤t); if (MatchString("0x", ¤t) && MatchChar(std::isxdigit, ¤t)) { while (MatchChar(std::isxdigit, ¤t)) { } *pos = current; return true; } return false; } static bool MatchDecimalLiteral(InputPosition* pos) { InputPosition current = *pos; bool found_digit = false; MatchString("-", ¤t); while (MatchChar(std::isdigit, ¤t)) found_digit = true; MatchString(".", ¤t); while (MatchChar(std::isdigit, ¤t)) found_digit = true; if (!found_digit) return false; *pos = current; if ((MatchString("e", ¤t) || MatchString("E", ¤t)) && (MatchString("+", ¤t) || MatchString("-", ¤t) || true) && MatchChar(std::isdigit, ¤t)) { while (MatchChar(std::isdigit, ¤t)) { } *pos = current; return true; } return true; } public: JsonGrammar() : Grammar(&file) { SetWhitespace(MatchWhitespace); } Symbol trueLiteral = {Rule({Token("true")})}; Symbol falseLiteral = {Rule({Token("false")})}; Symbol nullLiteral = {Rule({Token("null")})}; Symbol decimalLiteral = { Rule({Pattern(MatchDecimalLiteral)}, YieldMatchedInput), Rule({Pattern(MatchHexLiteral)}, YieldMatchedInput)}; Symbol stringLiteral = { Rule({Pattern(MatchStringLiteral)}, YieldMatchedInput)}; Symbol* elementList = List<JsonValue>(&value, Token(",")); Symbol array = {Rule({Token("["), elementList, Token("]")})}; Symbol member = {Rule({&stringLiteral, Token(":"), &value}, MakeMember)}; Symbol* memberList = List<JsonMember>(&member, Token(",")); Symbol object = {Rule({Token("{"), memberList, Token("}")})}; Symbol value = {Rule({&trueLiteral}, MakeBoolLiteral<true>), Rule({&falseLiteral}, MakeBoolLiteral<false>), Rule({&nullLiteral}, MakeNullLiteral), Rule({&decimalLiteral}, MakeNumberLiteral), Rule({&stringLiteral}, MakeStringLiteral), Rule({&object}, MakeObject), Rule({&array}, MakeArray)}; Symbol file = {Rule({&value})}; }; JsonParserResult ParseJson(const std::string& input) { // Torque needs a CurrentSourceFile scope during parsing. // As JSON lives in memory only, a unknown file scope is created. SourceFileMap::Scope source_map_scope; CurrentSourceFile::Scope unkown_file(SourceFileMap::AddSource("<json>")); JsonParserResult result; try { result.value = (*JsonGrammar().Parse(input)).Cast<JsonValue>(); } catch (TorqueError& error) { result.error = error; } return result; } } // namespace ls } // namespace torque } // namespace internal } // namespace v8