ParseResult Coalesce::parse(const Convertible& value, ParsingContext& ctx) { assert(isArray(value)); auto length = arrayLength(value); if (length < 2) { ctx.error("Expected at least one argument."); return ParseResult(); } optional<type::Type> outputType; if (ctx.getExpected() && *ctx.getExpected() != type::Value) { outputType = ctx.getExpected(); } Coalesce::Args args; args.reserve(length - 1); for (std::size_t i = 1; i < length; i++) { auto parsed = ctx.parse(arrayMember(value, i), i, outputType); if (!parsed) { return parsed; } if (!outputType) { outputType = (*parsed)->getType(); } args.push_back(std::move(*parsed)); } assert(outputType); return ParseResult(std::make_unique<Coalesce>(*outputType, std::move(args))); }
ParseResult At::parse(const Convertible& value, ParsingContext& ctx) { assert(isArray(value)); std::size_t length = arrayLength(value); if (length != 3) { ctx.error("Expected 2 arguments, but found " + util::toString(length - 1) + " instead."); return ParseResult(); } ParseResult index = ctx.parse(arrayMember(value, 1), 1, {type::Number}); type::Type inputType = type::Array(ctx.getExpected() ? *ctx.getExpected() : type::Value); ParseResult input = ctx.parse(arrayMember(value, 2), 2, {inputType}); if (!index || !input) return ParseResult(); return ParseResult(std::make_unique<At>(std::move(*index), std::move(*input))); }
ParseResult Let::parse(const Convertible& value, ParsingContext& ctx) { assert(isArray(value)); std::size_t length = arrayLength(value); if (length < 4) { ctx.error("Expected at least 3 arguments, but found " + util::toString(length - 1) + " instead."); return ParseResult(); } std::map<std::string, std::shared_ptr<Expression>> bindings_; for(std::size_t i = 1; i < length - 1; i += 2) { optional<std::string> name = toString(arrayMember(value, i)); if (!name) { ctx.error("Expected string, but found " + getJSONType(arrayMember(value, i)) + " instead.", i); return ParseResult(); } bool isValidName = std::all_of(name->begin(), name->end(), [](unsigned char c) { return ::isalnum(c) || c == '_'; }); if (!isValidName) { ctx.error("Variable names must contain only alphanumeric characters or '_'.", 1); return ParseResult(); } ParseResult bindingValue = ctx.parse(arrayMember(value, i + 1), i + 1); if (!bindingValue) { return ParseResult(); } bindings_.emplace(*name, std::move(*bindingValue)); } ParseResult result_ = ctx.parse(arrayMember(value, length - 1), length - 1, ctx.getExpected(), bindings_); if (!result_) { return ParseResult(); } return ParseResult(std::make_unique<Let>(std::move(bindings_), std::move(*result_))); }
ParseResult parseMatch(const Convertible& value, ParsingContext& ctx) { assert(isArray(value)); auto length = arrayLength(value); if (length < 5) { ctx.error( "Expected at least 4 arguments, but found only " + util::toString(length - 1) + "." ); return ParseResult(); } // Expect odd-length array: ["match", input, 2 * (n pairs)..., otherwise] if (length % 2 != 1) { ctx.error("Expected an even number of arguments."); return ParseResult(); } optional<type::Type> inputType; optional<type::Type> outputType; if (ctx.getExpected() && *ctx.getExpected() != type::Value) { outputType = ctx.getExpected(); } std::vector<std::pair<std::vector<InputType>, std::unique_ptr<Expression>>> branches; branches.reserve((length - 3) / 2); for (size_t i = 2; i + 1 < length; i += 2) { const auto& label = arrayMember(value, i); std::vector<InputType> labels; // Match pair inputs are provided as either a literal value or a // raw JSON array of string / number / boolean values. if (isArray(label)) { auto groupLength = arrayLength(label); if (groupLength == 0) { ctx.error("Expected at least one branch label.", i); return ParseResult(); } labels.reserve(groupLength); for (size_t j = 0; j < groupLength; j++) { const optional<InputType> inputValue = parseInputValue(arrayMember(label, j), ctx, i, inputType); if (!inputValue) { return ParseResult(); } labels.push_back(*inputValue); } } else { const optional<InputType> inputValue = parseInputValue(label, ctx, i, inputType); if (!inputValue) { return ParseResult(); } labels.push_back(*inputValue); } ParseResult output = ctx.parse(arrayMember(value, i + 1), i + 1, outputType); if (!output) { return ParseResult(); } if (!outputType) { outputType = (*output)->getType(); } branches.emplace_back(std::move(labels), std::move(*output)); } auto input = ctx.parse(arrayMember(value, 1), 1, {type::Value}); if (!input) { return ParseResult(); } auto otherwise = ctx.parse(arrayMember(value, length - 1), length - 1, outputType); if (!otherwise) { return ParseResult(); } assert(inputType && outputType); optional<std::string> err; if ((*input)->getType() != type::Value && (err = type::checkSubtype(*inputType, (*input)->getType()))) { ctx.error(*err, 1); return ParseResult(); } return inputType->match( [&](const type::NumberType&) { return create<int64_t>(*outputType, std::move(*input), std::move(branches), std::move(*otherwise), ctx); }, [&](const type::StringType&) { return create<std::string>(*outputType, std::move(*input), std::move(branches), std::move(*otherwise), ctx); }, [&](const auto&) { // unreachable: inputType is set by parseInputValue(), which only // accepts string and (integer) numeric values. assert(false); return ParseResult(); } ); }