protected Op.Pair<Double, Double> extractRange(String line) {
        List<String> token = Op.split(line, ":=");
        if (token.size() != 2) {
            throw new RuntimeException("[syntax error] "
                    + "expected property of type (key := value) in line: " + line);
        }

        String rangeToken = token.get(1);

        String range = "";
        for (char c : rangeToken.toCharArray()) {
            if (c == '(' || c == ')' || c == ' ' || c == ';') {
                continue;
            }
            range += c;
        }

        token = Op.split(range, "..");
        if (token.size() != 2) {
            throw new RuntimeException(String.format("[syntax error] expected property of type"
                    + " 'start .. end', but found <%s> in line: %s", range, line));
        }

        double minimum, maximum;
        int index = -1;
        try {
            minimum = Op.toDouble(token.get(index = 0));
            maximum = Op.toDouble(token.get(index = 1));
        } catch (Exception ex) {
            throw new RuntimeException(String.format(
                    "[syntax error] expected numeric value, but found <%s> in line %s",
                    token.get(index), line));
        }

        return new Op.Pair<>(minimum, maximum);
    }