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); }