Beispiel #1
0
css::time css::parse_smil_time(const buffer &aValue)
{
    if (aValue.empty()) return time();

    uint8_t state = (uint8_t)time_unit_state::value;
    int value = 0; // the value to set the time to
    int accumulator = 0; // the value of the current block
    int digits = 0; // the number of digits in the current block
    int divisor = 1; // the number to divide by to convert value to a fraction
    int groups = 1; // the number of time groups (separated by a ':' character)
    bool is_fraction = false;
    for (char c : aValue) switch (c)
        {
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
            switch ((time_unit_state)state)
            {
            case time_unit_state::value:
            case time_unit_state::clock:
                accumulator *= 10;
                accumulator += (c - '0');
                if (is_fraction)
                {
                    value   *= 10;
                    divisor *= 10;
                }
                else
                    ++digits;
                break;
            default:
                state = (uint8_t)time_unit_state::invalid;
                break;
            }
            break;
        case ':':
            switch ((time_unit_state)state)
            {
            case time_unit_state::value:
            case time_unit_state::clock:
                state = (uint8_t)time_unit_state::clock;
                break;
            default:
                state = (uint8_t)time_unit_state::invalid;
                break;
            }

            if (groups == 3)
                throw std::runtime_error("clock values cannot specify days (i.e. have 3 ':' characters)");
            if (digits == 0)
                throw std::runtime_error("no value in a hours, minutes or seconds block");
            if (groups > 1 && digits != 2)
                throw std::runtime_error("an hours, minutes or seconds block must have 2 digits");
            value += accumulator;
            value *= 60;
            accumulator = 0;
            digits = 0;
            ++groups;
            break;
        case '.':
            switch ((time_unit_state)state)
            {
            case time_unit_state::value:
            case time_unit_state::clock:
                if (is_fraction)
                    throw std::runtime_error("multiple '.' characters found in time string");
                is_fraction = true;
                break;
            default:
                state = (uint8_t)time_unit_state::invalid;
                break;
            }
            break;
        case 'a':
        case 'b':
        case 'c':
        case 'd':
        case 'e':
        case 'f':
        case 'g':
        case 'h':
        case 'i':
        case 'j':
        case 'k':
        case 'l':
        case 'm':
        case 'n':
        case 'o':
        case 'p':
        case 'q':
        case 'r':
        case 's':
        case 't':
        case 'u':
        case 'v':
        case 'w':
        case 'x':
        case 'y':
        case 'z':
            state = time_unit_state_transitions[state][c - 'a'];
            break;
        default:
            throw std::runtime_error("invalid character found in time string");
        }

    if (groups > 1)
    {
        if (digits == 0)
            throw std::runtime_error("no value in a hours, minutes or seconds block");
        if (digits != 2)
            throw std::runtime_error("an hours, minutes or seconds block must have 2 digits");
    }

    float time_value = float(value + accumulator) / divisor;
    switch ((time_unit_state)state)
    {
    case time_unit_state::hours:
        return css::time(time_value * 3600.0, css::time::seconds);
    case time_unit_state::minutes:
        return css::time(time_value * 60.0, css::time::seconds);
    case time_unit_state::value:
    case time_unit_state::clock:
    case time_unit_state::seconds:
        return css::time(time_value, css::time::seconds);
    case time_unit_state::milliseconds:
        return css::time(time_value, css::time::milliseconds);
    default:
        throw std::runtime_error("invalid character found in time string");
    }
}
static notation_entry parse_notation_core(parser & p, bool overload, notation_entry_group grp, buffer<token_entry> & new_tokens, bool parse_only,
                                          unsigned priority) {
    buffer<expr>       locals;
    buffer<transition> ts;
    parser::local_scope scope(p);
    bool is_nud = true;
    optional<parse_table> pt;
    optional<parse_table> reserved_pt;
    if (p.curr_is_numeral()) {
        lean_assert(p.curr_is_numeral());
        mpz num = p.get_num_val().get_numerator();
        p.next();
        p.check_token_next(get_assign_tk(), "invalid numeral notation, `:=` expected");
        auto e_pos = p.pos();
        expr e     = p.parse_expr();
        check_notation_expr(e, e_pos);
        return notation_entry(num, e, overload, parse_only);
    } else if (p.curr_is_identifier()) {
        parse_notation_local(p, locals);
        is_nud = false;
        pt = get_led_table(p.env());
        if (grp != notation_entry_group::Reserve)
            reserved_pt = get_reserved_led_table(p.env());
    } else {
        pt = get_nud_table(p.env());
        if (grp != notation_entry_group::Reserve)
            reserved_pt = get_reserved_nud_table(p.env());
    }
    bool used_default = false;
    while ((grp != notation_entry_group::Reserve && !p.curr_is_token(get_assign_tk())) ||
           (grp == notation_entry_group::Reserve && !p.curr_is_command() && !p.curr_is_eof())) {
        name pp_tk = parse_quoted_symbol_or_token(p, new_tokens, used_default, grp).to_string();
        name tk = utf8_trim(pp_tk.to_string());
        if (auto at = find_next(reserved_pt, tk)) {
            // Remark: we are ignoring multiple actions in the reserved notation table
            transition const & trans = head(at).first;
            action const & a = trans.get_action();
            reserved_pt      = head(at).second;
            if (!p.curr_is_quoted_symbol())
                pp_tk = trans.get_pp_token();
            switch (a.kind()) {
            case notation::action_kind::Skip:
                if (!p.curr_is_quoted_symbol() && !p.curr_is_keyword() && !p.curr_is_token(get_assign_tk())) {
                    if (g_allow_local && !p.curr_is_token_or_id(get_binders_tk())) {
                        ts.push_back(parse_transition(p, pt, tk, locals, new_tokens, grp, pp_tk));
                        break;
                    }
                    p.check_token_or_id_next(get_binders_tk(),
                                             "invalid notation declaration, quoted-symbol, keyword or `:=` expected "
                                             "(declaration prefix matches reserved notation)");
                }
                ts.push_back(transition(tk, a, pp_tk));
                break;
            case notation::action_kind::Binder:
                if (g_allow_local && !p.curr_is_token_or_id(get_binder_tk())) {
                    ts.push_back(parse_transition(p, pt, tk, locals, new_tokens, grp, pp_tk));
                    break;
                }
                p.check_token_or_id_next(get_binder_tk(),
                                         "invalid notation declaration, 'binder' expected "
                                         "(declaration prefix matches reserved notation)");
                ts.push_back(transition(tk, a, pp_tk));
                break;
            case notation::action_kind::Binders:
                if (g_allow_local && !p.curr_is_token_or_id(get_binders_tk())) {
                    ts.push_back(parse_transition(p, pt, tk, locals, new_tokens, grp, pp_tk));
                    break;
                }
                p.check_token_or_id_next(get_binders_tk(),
                                         "invalid notation declaration, 'binders' expected "
                                         "(declaration prefix matches reserved notation)");
                ts.push_back(transition(tk, a, pp_tk));
                break;
            case notation::action_kind::Expr: case notation::action_kind::Exprs: case notation::action_kind::ScopedExpr:
            case notation::action_kind::Ext:  {
                if (g_allow_local && !p.curr_is_identifier()) {
                    ts.push_back(parse_transition(p, pt, tk, locals, new_tokens, grp, pp_tk));
                    break;
                }
                name n = p.check_id_next("invalid notation declaration, identifier expected "
                                         "(declaration prefix matches reserved notation)");
                if (p.curr_is_token(get_colon_tk())) {
                    if (g_allow_local) {
                        unsigned default_prec = get_default_prec(pt, tk);
                        action a = parse_action(p, tk, default_prec, locals, new_tokens, grp);
                        expr local_type = mk_Prop(); // type used in notation local declarations, it is irrelevant
                        expr l = mk_local(n, local_type);
                        p.add_local(l);
                        locals.push_back(l);
                        ts.push_back(transition(tk, a, pp_tk));
                        break;
                    } else {
                        throw parser_error("invalid notation declaration, invalid ':' occurrence "
                                           "(declaration prefix matches reserved notation)", p.pos());
                    }
                } else {
                    expr local_type = mk_Prop(); // type used in notation local declarations, it is irrelevant
                    expr l = mk_local(n, local_type);
                    p.add_local(l);
                    locals.push_back(l);
                    ts.push_back(transition(tk, a, pp_tk));
                    break;
                }
            }}
        } else {
            reserved_pt = optional<parse_table>();
            ts.push_back(parse_transition(p, pt, tk, locals, new_tokens, grp, pp_tk));
        }
        pt = find_match(pt, ts.back());
    }
    // for atomic notation where binding power was not set, we set it to max
    if (used_default && ts.size() == 1 && ts.back().get_action().kind() == notation::action_kind::Skip) {
        lean_assert(!new_tokens.empty());
        new_tokens.back().m_prec = get_max_prec();
    }
    expr n;
    if (grp == notation_entry_group::Reserve) {
        // reserve notation commands do not have a denotation
        lean_assert(p.curr_is_command() || p.curr_is_eof());
        expr dummy = mk_Prop(); // any expression without free variables will do
        n = dummy;
    } else {
        lean_assert(p.curr_is_token(get_assign_tk()));
        p.next();
        if (ts.empty())
            throw parser_error("invalid notation declaration, empty notation is not allowed", p.pos());
        n = parse_notation_expr(p, locals);
    }
    return notation_entry(is_nud, to_list(ts.begin(), ts.end()), n, overload, priority, grp, parse_only);
}