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)));
}
Beispiel #2
0
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)));

}
Beispiel #3
0
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_)));
}
Beispiel #4
0
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();
        }
    );
}