Exemple #1
0
// using [class] id (id ...) (renaming id->id id->id) (hiding id ... id)
environment using_cmd(parser & p) {
    environment env = p.env();
    while (true) {
        name cls = parse_class(p);
        bool decls = cls.is_anonymous() || cls == g_decls || cls == g_declarations;
        auto pos   = p.pos();
        name ns    = p.check_id_next("invalid 'using' command, identifier expected");
        optional<name> real_ns = to_valid_namespace_name(env, ns);
        if (!real_ns)
            throw parser_error(sstream() << "invalid namespace name '" << ns << "'", pos);
        ns = *real_ns;
        env = using_namespace(env, p.ios(), ns, cls);
        if (decls) {
            // Remark: we currently to not allow renaming and hiding of universe levels
            buffer<name> exceptions;
            bool found_explicit = false;
            while (p.curr_is_token(g_lparen)) {
                p.next();
                if (p.curr_is_token_or_id(g_renaming)) {
                    p.next();
                    while (p.curr_is_identifier()) {
                        name from_id = p.get_name_val();
                        p.next();
                        p.check_token_next(g_arrow, "invalid 'using' command renaming, '->' expected");
                        name to_id = p.check_id_next("invalid 'using' command renaming, identifier expected");
                        check_identifier(p, env, ns, from_id);
                        exceptions.push_back(from_id);
                        env = add_expr_alias(env, to_id, ns+from_id);
                    }
                } else if (p.curr_is_token_or_id(g_hiding)) {
                    p.next();
                    while (p.curr_is_identifier()) {
                        name id = p.get_name_val();
                        p.next();
                        check_identifier(p, env, ns, id);
                        exceptions.push_back(id);
                    }
                } else if (p.curr_is_identifier()) {
                    found_explicit = true;
                    while (p.curr_is_identifier()) {
                        name id = p.get_name_val();
                        p.next();
                        check_identifier(p, env, ns, id);
                        env = add_expr_alias(env, id, ns+id);
                    }
                } else {
                    throw parser_error("invalid 'using' command option, identifier, 'hiding' or 'renaming' expected", p.pos());
                }
                if (found_explicit && !exceptions.empty())
                    throw parser_error("invalid 'using' command option, mixing explicit and implicit 'using' options", p.pos());
                p.check_token_next(g_rparen, "invalid 'using' command option, ')' expected");
            }
            if (!found_explicit)
                env = add_aliases(env, ns, name(), exceptions.size(), exceptions.data());
        }
        if (!p.curr_is_token(g_lbracket) && !p.curr_is_identifier())
            break;
    }
    return env;
}
Exemple #2
0
environment opaque_hint_cmd(parser & p) {
    environment env = p.env();
    bool found = false;
    while (p.curr_is_token(g_lparen)) {
        p.next();
        bool hiding;
        auto pos = p.pos();
        if (p.curr_is_token_or_id(g_hiding))
            hiding = true;
        else if (p.curr_is_token_or_id(g_exposing))
            hiding = false;
        else
            throw parser_error("invalid 'opaque_hint', 'hiding' or 'exposing' expected", pos);
        p.next();
        while (!p.curr_is_token(g_rparen)) {
            if (p.curr_is_token(g_module)) {
                found = true;
                p.next();
                env = set_hide_main_opaque(env, hiding);
            } else {
                name c  = p.check_constant_next("invalid 'opaque_hint', constant expected");
                found   = true;
                if (hiding)
                    env = hide_definition(env, c);
                else
                    env = expose_definition(env, c);
            }
        }
        p.next();
    }
    if (!found)
        throw exception("invalid empty 'opaque_hint' command");
    return env;
}
implicit_infer_kind parse_implicit_infer_modifier(parser & p) {
    if (p.curr_is_token(get_lcurly_tk())) {
        p.next();
        p.check_token_next(get_rcurly_tk(), "invalid introduction rule, '}' expected");
        return implicit_infer_kind::RelaxedImplicit;
    } else if (p.curr_is_token(get_lparen_tk())) {
        p.next();
        p.check_token_next(get_rparen_tk(), "invalid introduction rule, ')' expected");
        return implicit_infer_kind::None;
    } else {
        return implicit_infer_kind::Implicit;
    }
}
Exemple #4
0
static void parse_filters(parser & p, buffer<std::string> & pos_names, buffer<std::string> & neg_names) {
    name plus("+"); name minus("-");
    while (p.curr_is_token(get_comma_tk())) {
        p.next();
        if (p.curr_is_token(plus)) {
            p.next();
            pos_names.push_back(p.check_id_next("invalid find_decl command, identifier expected").to_string());
        } else if (p.curr_is_token(minus)) {
            p.next();
            neg_names.push_back(p.check_id_next("invalid find_decl command, identifier expected").to_string());
        } else {
            pos_names.push_back(p.check_id_next("invalid find_decl command, '+', '-', or identifier expected").to_string());
        }
    }
}
static transition parse_transition(parser & p, optional<parse_table> const & pt, name const & tk,
                                   buffer<expr> & locals, buffer<token_entry> & new_tokens, notation_entry_group grp,
                                   name const & pp_tk) {
    if (p.curr_is_token_or_id(get_binder_tk())) {
        p.next();
        unsigned rbp = parse_binders_rbp(p);
        return transition(tk, mk_binder_action(rbp), pp_tk);
    } else if (p.curr_is_token_or_id(get_binders_tk())) {
        p.next();
        unsigned rbp = parse_binders_rbp(p);
        return transition(tk, mk_binders_action(rbp), pp_tk);
    } else if (p.curr_is_identifier()) {
        unsigned default_prec = get_default_prec(pt, tk);
        name n   = p.get_name_val();
        p.next();
        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);
        return transition(tk, a, pp_tk);
    } else if (p.curr_is_quoted_symbol() || p.curr_is_keyword() ||
               p.curr_is_token(get_assign_tk()) || p.curr_is_command() || p.curr_is_eof()) {
        return transition(tk, mk_skip_action(), pp_tk);
    } else {
        throw parser_error("invalid notation declaration, quoted-symbol, identifier, "
                           "'binder', 'binders' expected", p.pos());
    }
}
static expr parse_struct_expr_core(parser & p, pos_info const & pos, bool curly_bar) {
    expr t = p.parse_expr();
    buffer<name> field_names;
    buffer<expr> field_values;
    buffer<expr> using_exprs;
    while (p.curr_is_token(get_comma_tk())) {
        p.next();
        pair<optional<name>, expr> id_e = p.parse_optional_assignment();
        if (id_e.first) {
            field_names.push_back(*id_e.first);
            field_values.push_back(id_e.second);
        } else {
            using_exprs.push_back(id_e.second);
        }
    }
    if (curly_bar)
        p.check_token_next(get_rcurlybar_tk(), "invalid structure expression, '|}' expected");
    else
        p.check_token_next(get_rdcurly_tk(), "invalid structure expression, '⦄' expected");
    buffer<expr> args;
    args.push_back(t);
    args.append(field_values);
    args.append(using_exprs);
    return p.save_pos(mk_structure_instance(to_list(field_names), args.size(), args.data()), pos);
}
static name parse_quoted_symbol_or_token(parser & p, buffer<token_entry> & new_tokens, bool & used_default, notation_entry_group grp) {
    used_default = false;
    if (p.curr_is_quoted_symbol()) {
        environment const & env = p.env();
        auto pp_tk = p.get_name_val();
        auto tks   = utf8_trim(pp_tk.to_string());
        auto tkcs  = tks.c_str();
        check_not_forbidden(tkcs);
        p.next();
        if (p.curr_is_token(get_colon_tk())) {
            p.next();
            unsigned prec = parse_precedence(p);
            new_tokens.push_back(mk_token_entry(tkcs, prec, grp));
        } else if (!get_precedence(env, tkcs, grp)) {
            new_tokens.push_back(mk_token_entry(tkcs, LEAN_DEFAULT_PRECEDENCE, grp));
            used_default = true;
        }
        return pp_tk;
    } else if (p.curr_is_keyword()) {
        auto tk = p.get_token_info().token();
        check_not_forbidden(tk.to_string().c_str());
        p.next();
        return tk;
    } else {
        throw parser_error("invalid notation declaration, symbol expected", p.pos());
    }
}
static unsigned parse_binders_rbp(parser & p) {
    if (p.curr_is_token(get_colon_tk())) {
        p.next();
        return parse_precedence(p);
    } else {
        return 0;
    }
}
static optional<unsigned> parse_optional_precedence(parser & p) {
    if (p.curr_is_token(get_colon_tk())) {
        p.next();
        return some(parse_precedence_core(p));
    } else {
        return optional<unsigned>();
    }
}
Exemple #10
0
void type_modifiers::parse(parser & p) {
    while (true) {
        if (p.curr_is_token(get_class_tk())) {
            m_is_class = true;
            p.next();
        } else {
            break;
        }
    }
}
 void parse(parser & p) {
     while (true) {
         if (p.curr_is_token(get_parsing_only_tk())) {
             p.next();
             m_parse_only = true;
         } else if (auto prio = parse_priority(p)) {
             m_priority = *prio;
         } else {
             return;
         }
     }
 }
static notation_entry parse_notation(parser & p, bool overload, notation_entry_group grp, buffer<token_entry> & new_tokens,
                                     bool allow_local) {
    bool parse_only   = false;
    unsigned priority = LEAN_DEFAULT_NOTATION_PRIORITY;
    flet<bool> set_allow_local(g_allow_local, allow_local);
    if (p.curr_is_token(get_infix_tk()) || p.curr_is_token(get_infixl_tk())) {
        p.next();
        return parse_mixfix_notation(p, mixfix_kind::infixl, overload, grp, new_tokens, parse_only, priority);
    } else if (p.curr_is_token(get_infixr_tk())) {
        p.next();
        return parse_mixfix_notation(p, mixfix_kind::infixr, overload, grp, new_tokens, parse_only, priority);
    } else if (p.curr_is_token(get_postfix_tk())) {
        p.next();
        return parse_mixfix_notation(p, mixfix_kind::postfix, overload, grp, new_tokens, parse_only, priority);
    } else if (p.curr_is_token(get_prefix_tk())) {
        p.next();
        return parse_mixfix_notation(p, mixfix_kind::prefix, overload, grp, new_tokens, parse_only, priority);
    } else if (p.curr_is_token(get_notation_tk())) {
        p.next();
        return parse_notation_core(p, overload, grp, new_tokens, parse_only, priority);
    } else {
        throw parser_error("invalid notation, 'infix', 'infixl', 'infixr', 'prefix', "
                           "'postfix' or 'notation' expected", p.pos());
    }
}
Exemple #13
0
environment coercion_cmd(parser & p) {
    auto pos = p.pos();
    expr f   = p.parse_expr();
    if (!is_constant(f))
        throw parser_error("invalid 'coercion' command, constant expected", pos);
    if (p.curr_is_token(g_colon)) {
        p.next();
        pos = p.pos();
        expr C = p.parse_expr();
        if (!is_constant(C))
            throw parser_error("invalid 'coercion' command, constant expected", pos);
        return add_coercion(p.env(), const_name(f), const_name(C), p.ios());
    } else {
        return add_coercion(p.env(), const_name(f), p.ios());
    }
}
Exemple #14
0
static name parse_class(parser & p) {
    if (p.curr_is_token(g_lbracket)) {
        p.next();
        name n;
        if (p.curr_is_identifier())
            n = p.get_name_val();
        else if (p.curr_is_keyword() || p.curr_is_command())
            n = p.get_token_info().value();
        else
            throw parser_error("invalid 'using' command, identifier or symbol expected", p.pos());
        p.next();
        p.check_token_next(g_rbracket, "invalid 'using' command, ']' expected");
        return n;
    } else {
        return name();
    }
}
Exemple #15
0
expr parse_nested_declaration(parser & p, unsigned, expr const *, pos_info const & pos) {
    try {
        optional<name> n;
        decl_attributes attrs;
        if (!g_allow_nested_declarations)
            throw parser_error("invalid 'abstract' expression, it is only allowed inside definitions", pos);
        if (p.curr_is_token(get_as_tk())) {
            p.next();
            n = p.check_id_next("invalid 'abstract' expression, identifier expected");
        }
        attrs.parse(p);
        expr e = p.parse_expr();
        p.check_token_next(get_end_tk(), "invalid 'abstract' expression, 'end' expected");
        return p.save_pos(mk_nested_declaration(n, attrs, e), pos);
    } catch (exception & ex) {
        consume_until_end(p);
        ex.rethrow();
        lean_unreachable();
    }
}
// auxiliary procedure used by local_notation_cmd and reserve_cmd
static environment dispatch_notation_cmd(parser & p, bool overload, notation_entry_group grp, bool persistent) {
    if (p.curr_is_token(get_notation_tk())) {
        p.next();
        return notation_cmd_core(p, overload, grp, persistent);
    } else if (p.curr_is_token(get_infixl_tk())) {
        p.next();
        return mixfix_cmd(p, mixfix_kind::infixl, overload, grp, persistent);
    } else if (p.curr_is_token(get_infix_tk())) {
        p.next();
        return mixfix_cmd(p, mixfix_kind::infixl, overload, grp, persistent);
    } else if (p.curr_is_token(get_infixr_tk())) {
        p.next();
        return mixfix_cmd(p, mixfix_kind::infixr, overload, grp, persistent);
    } else if (p.curr_is_token(get_prefix_tk())) {
        p.next();
        return mixfix_cmd(p, mixfix_kind::prefix, overload, grp, persistent);
    } else if (p.curr_is_token(get_postfix_tk())) {
        p.next();
        return mixfix_cmd(p, mixfix_kind::postfix, overload, grp, persistent);
    } else {
        throw parser_error("invalid local/reserve notation, 'infix', 'infixl', 'infixr', 'prefix', "
                           "'postfix' or 'notation' expected", p.pos());
    }
}
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);
}
Exemple #18
0
void decl_attributes::parse(parser & p) {
    buffer<char const *> attr_tokens;
    get_attribute_tokens(attr_tokens);
    while (true) {
        auto pos   = p.pos();
        if (auto it = parse_priority(p)) {
            m_prio = *it;
            bool has_prio_attr = false;
            for (auto const & entry : m_entries) {
                if (get_attribute_kind(entry.m_attr.c_str()) == attribute_kind::Prioritized) {
                    has_prio_attr = true;
                    break;
                }
            }
            if (!has_prio_attr) {
                throw parser_error("invalid '[priority]' attribute, declaration has not been marked with a prioritized attribute", pos);
            }
        } else if (p.curr_is_token(get_parsing_only_tk())) {
            if (!m_is_abbrev)
                throw parser_error("invalid '[parsing_only]' attribute, only abbreviations can be "
                                   "marked as '[parsing_only]'", pos);
            m_parsing_only = true;
            p.next();
        } else {
            bool found = false;
            for (char const * tk : attr_tokens) {
                if (p.curr_is_token(tk)) {
                    p.next();
                    char const * attr = get_attribute_from_token(tk);
                    for (auto const & entry : m_entries) {
                        if (are_incompatible(entry.m_attr.c_str(), attr)) {
                            throw parser_error(sstream() << "invalid attribute [" << attr
                                               << "], declaration was already marked with [" << entry.m_attr << "]", pos);
                        }
                    }
                    switch (get_attribute_kind(attr)) {
                    case attribute_kind::Default:
                    case attribute_kind::Prioritized:
                        m_entries = cons(entry(attr), m_entries);
                        break;
                    case attribute_kind::Parametric: {
                        unsigned v = p.parse_small_nat();
                        if (v == 0)
                            throw parser_error("invalid attribute parameter, value must be positive", pos);
                        p.check_token_next(get_rbracket_tk(), "invalid attribute, ']' expected");
                        m_entries = cons(entry(attr, v-1), m_entries);
                        break;
                    }
                    case attribute_kind::OptParametric:
                        if (!p.curr_is_token(get_rbracket_tk())) {
                            unsigned v = p.parse_small_nat();
                            if (v == 0)
                                throw parser_error("invalid attribute parameter, value must be positive", pos);
                            p.check_token_next(get_rbracket_tk(), "invalid attribute, ']' expected");
                            m_entries = cons(entry(attr, v-1), m_entries);
                        } else {
                            p.check_token_next(get_rbracket_tk(), "invalid attribute, ']' expected");
                            m_entries = cons(entry(attr), m_entries);
                        }
                        break;
                    case attribute_kind::MultiParametric: {
                        buffer<unsigned> vs;
                        while (true) {
                            unsigned v = p.parse_small_nat();
                            if (v == 0)
                                throw parser_error("invalid attribute parameter, value must be positive", pos);
                            vs.push_back(v-1);
                            if (p.curr_is_token(get_rbracket_tk()))
                                break;
                        }
                        p.next();
                        m_entries = cons(entry(attr, to_list(vs)), m_entries);
                        break;
                    }
                    }
                    found = true;
                    break;
                }
            }
            if (!found)
                break;
        }
    }
}
bool curr_is_notation_decl(parser & p) {
    return
        p.curr_is_token(get_infix_tk()) || p.curr_is_token(get_infixl_tk()) || p.curr_is_token(get_infixr_tk()) ||
        p.curr_is_token(get_postfix_tk()) || p.curr_is_token(get_prefix_tk()) || p.curr_is_token(get_notation_tk());
}
static action parse_action(parser & p, name const & prev_token, unsigned default_prec,
                           buffer<expr> & locals, buffer<token_entry> & new_tokens, notation_entry_group grp) {
    if (p.curr_is_token(get_colon_tk())) {
        p.next();
        if (p.curr_is_numeral() || p.curr_is_token_or_id(get_max_tk())) {
            unsigned prec = parse_precedence(p);
            return mk_expr_action(prec);
        } else if (p.curr_is_token_or_id(get_prev_tk())) {
            p.next();
            return mk_expr_action(get_precedence(p.env(), new_tokens, prev_token));
        } else if (p.curr_is_token_or_id(get_scoped_tk())) {
            p.next();
            return mk_scoped_expr_action(mk_var(0));
        } else {
            p.check_token_next(get_lparen_tk(), "invalid notation declaration, '(', numeral or 'scoped' expected");
            if (p.curr_is_token_or_id(get_foldl_tk()) || p.curr_is_token_or_id(get_foldr_tk())) {
                bool is_fold_right = p.curr_is_token_or_id(get_foldr_tk());
                p.next();
                auto prec = parse_optional_precedence(p);
                name sep  = parse_quoted_symbol_or_token(p, new_tokens, grp);
                expr rec;
                {
                    parser::local_scope scope(p);
                    p.check_token_next(get_lparen_tk(), "invalid fold notation argument, '(' expected");
                    parse_notation_local(p, locals);
                    parse_notation_local(p, locals);
                    p.check_token_next(get_comma_tk(),  "invalid fold notation argument, ',' expected");
                    rec  = parse_notation_expr(p, locals);
                    p.check_token_next(get_rparen_tk(), "invalid fold notation argument, ')' expected");
                    locals.pop_back();
                    locals.pop_back();
                }
                optional<expr> ini;
                if (!p.curr_is_token(get_rparen_tk()) && !p.curr_is_quoted_symbol())
                    ini = parse_notation_expr(p, locals);
                optional<name> terminator;
                if (!p.curr_is_token(get_rparen_tk()))
                    terminator = parse_quoted_symbol_or_token(p, new_tokens, grp);
                p.check_token_next(get_rparen_tk(), "invalid fold notation argument, ')' expected");
                return mk_exprs_action(sep, rec, ini, terminator, is_fold_right, prec ? *prec : 0);
            } else if (p.curr_is_token_or_id(get_scoped_tk())) {
                p.next();
                auto prec = parse_optional_precedence(p);
                expr rec;
                {
                    parser::local_scope scope(p);
                    parse_notation_local(p, locals);
                    p.check_token_next(get_comma_tk(),  "invalid scoped notation argument, ',' expected");
                    rec  = parse_notation_expr(p, locals);
                    locals.pop_back();
                }
                p.check_token_next(get_rparen_tk(), "invalid scoped notation argument, ')' expected");
                return mk_scoped_expr_action(rec, prec ? *prec : 0);
            } else {
                throw parser_error("invalid notation declaration, 'foldl', 'foldr' or 'scoped' expected", p.pos());
            }
        }
    } else {
        return mk_expr_action(default_prec);
    }
}