// Parse a template declaration. // // tempate-declaration: // 'template' '<' template-parameter-list '>' [requires-clause] declaration // // FIXME: Support explicit template instantiations in one way or // another. Decl& Parser::template_declaration() { #if 0 require(tk::template_tok); // Build a psuedo-scope. // // FIXME: Merge this with template parameter scope. I don't think // that it's serving a very useful purpose. // Template_scope& tmp = cxt.make_template_scope(); Enter_scope tscope(cxt, tmp); // TODO: Allow >> to close the template parameter list in the // case of default template arguments. Enter_template_parameter_scope pscope(cxt); match(tk::lt_tok); tmp.parms = template_parameter_list(); match(tk::gt_tok); // Parse the optional requires clause, followed by the parameterized // declaration. if (tk::next_token_is(tk::requires_tok)) { // TODO: How are dependent names resolved in a requires clause? tmp.cons = &requires_clause(); // Enter_scope cscope(cxt, cxt.make_constrained_scope(*tmp.cons)); Parsing_template save(*this, &tmp.parms, tmp.cons); return declaration(); } else { Parsing_template save(*this, &tmp.parms); return declaration(); } #endif lingo_unreachable(); }
Decl& specialize_function(Context& cxt, Template_decl& tmp, Function_decl& d, Substitution& sub) { #if 0 // Create the specialization name. Name& n = cxt.get_template_id(tmp, sub.arguments()); // Substitute through parameters. // // TODO: I think I need to re-establish name bindings during substitution // because we are going to be resolving types at the same time. This // means that I am going to have to move scoping facilities from the // parser to the context (which makes some sense). Decl_list parms; for (Decl& p1 : d.parameters()) { Decl& p2 = substitute(cxt, p1, sub); parms.push_back(p2); } // Substitute through the return type. Type& ret = substitute(cxt, d.return_type(), sub); // FIXME: I've just re-attached an uninstantiated definition // to the declaration. That is going to be a problem. // return cxt.make_function_declaration(n, parms, ret, d.definition()); #endif lingo_unreachable(); }
Def& Parser::on_deleted_definition(Decl& d) { // Def& def = build.make_deleted_definition(); // return define_entity(d, def); lingo_unreachable(); }
Def& Parser::on_defaulted_definition(Decl& d) { // Def& def = build.make_defaulted_definition(); // return define_function(d, def); lingo_unreachable(); }
// Parse a directive sequence. // // directive-seq // 'type' expression ';' // 'evaluate' expression ';' // 'resolve' postscript-expression ';' // 'instantiate' template-id ';' // 'satisfy' check-expr ';' // 'order' '.' 'template' template-id template-id ';' // 'order' '.' 'concept' concept-id concept-id ';' // 'inspect.expr' expression ';' void directive_seq(Parser& p) { // Parse the directive sequence as if in the global namespace. Enter_scope scope(p.cxt, p.cxt.global_namespace()); while (p.peek()) { // TODO: We know that the next token is an identifier. // There is a more efficient way of doing this. if (p.next_token_is("type")) type_directive(p); else if (p.next_token_is("evaluate")) evaluate_directive(p); else if (p.next_token_is("resolve")) resolve_directive(p); else if (p.next_token_is("instantiate")) instantiate_directive(p); else if (p.next_token_is("satisfy")) satisfy_directive(p); else if (p.next_token_is("order")) order_directive(p); else if (p.next_token_is("inspect")) inspect_directive(p); else lingo_unreachable(); // FIXME: Actually diagnose an error. } }
char const* get_spelling(Binary_op op) { switch (op) { case num_add_op: return "+"; case num_sub_op: return "-"; case num_mul_op: return "*"; case num_div_op: return "/"; case num_mod_op: return "%"; case bit_and_op: return "&"; case bit_or_op: return "|"; case bit_xor_op: return "^"; case bit_lsh_op: return "<<"; case bit_rsh_op: return ">>"; case rel_eq_op: return "=="; case rel_ne_op: return "!="; case rel_lt_op: return "<"; case rel_gt_op: return ">"; case rel_le_op: return "<="; case rel_ge_op: return ">="; case log_and_op: return "&&"; case log_or_op: return "||"; } lingo_unreachable("invalid binary operator ({})", (int)op); }
// Parse a requires expression. // // requires-expression: // 'requires' ['<' template-parameter-list '>'] ['(' parameter-list ')'] requires-body // Expr& Parser::requires_expression() { lingo_unreachable(); // Token tok = require(tk::requires_tok); // // Match template parameter. // // // // TODO: Introduce a new scope. // Decl_list tparms; // if (match_if(tk::lt_tok)) { // tparms = template_parameter_list(); // match(tk::gt_tok); // } // // Parse parameters in a new block scope. // // Enter_scope scope(cxt, cxt.make_requires_scope()); // Decl_list parms; // if (match_if(tk::lparen_tok)) { // parms = parameter_list(); // match(tk::rparen_tok); // } // match(tk::lbrace_tok); // Req_list reqs = usage_seq(); // match(tk::rbrace_tok); // return on_requires_expression(tok, tparms, parms, reqs); }
// Return a converted template argument. Term& initialize_template_parameter(Context& cxt, Decl_iter p0, Decl_iter& pi, Term_iter a0, Term_iter& ai) { // TODO: Trap kind/type errors and emit good diagnostics. Term* c; if (Type_parm* p = as<Type_parm>(&*pi)) c = &initialize_type_template_parameter(cxt, *p, *ai); else if (Value_parm* p = as<Value_parm>(&*pi)) c = &initialize_value_template_parameter(cxt, *p, *ai); else if (Template_parm* p = as<Template_parm>(&*pi)) c = &initialize_template_template_parameter(cxt, *p, *ai); else lingo_unreachable(); // TODO: Handle parameter packs. How would these be // represented? Note that this isn't just the non-advancement // of the parameter, but the packing of subsequent arguments // into a new pack argument. ++pi; ++ai; return *c; }
// Parse a declaration. // // declaration: // [specifier-seq] basic-declaration // // basic-declaration: // variable-declaration // function-declaration // type-declaration // concept-declaration Decl& Parser::declaration() { // Parse and cache the specifier sequences. specifier_seq(); switch (lookahead()) { case tk::var_tok: return variable_declaration(); case tk::def_tok: return function_declaration(); case tk::class_tok: return class_declaration(); case tk::concept_tok: lingo_unreachable(); case tk::super_tok: return super_declaration(); default: break; } throw Syntax_error("invalid declaration"); }
// Construct a reference to an unresolved (overloaded) member. static Expr& make_mem_overload_ref(Context& cxt, Expr& obj, Name& name, Decl_list&& ds) { lingo_unreachable(); // return cxt.make_reference(obj, name, std::move(ds)); }
// Assuming the type of e1 is untested and e2 has floating point // type, convert to the most precise floating point type. Expr_pair convert_to_common_float(Context& cxt, Expr& e1, Expr& e2) { Float_type& f2 = cast<Float_type>(e2.type()); // If e1 has float type, convert to the most precise type. if (has_floating_point_type(e1)) { Float_type& f1 = cast<Float_type>(e1.type()); if (f1.precision() < f2.precision()) { Expr& c = convert_to_wider_float(cxt, e1, f2); return {c, e2}; } if (f2.precision() < f1.precision()) { Expr& c = convert_to_wider_float(cxt, e2, f1); return {e1, c}; } lingo_unreachable(); } // If e1 has integer type, convert to e2. if (has_integer_type(e1)) { Expr& c = convert_integer_to_float(cxt, e1, f2); return {c, e2}; } error(cxt, "no floating point conversions for '{}' and '{}'", e1, e2); throw Type_error(); }
Def& Parser::on_function_definition(Decl& d, Stmt& s) { // Def& def = build.make_function_definition(s); // return define_function(d, def); lingo_unreachable(); }
Decl& Parser::on_concept_declaration(Token, Name& n, Decl_list& ps) { lingo_unreachable(); // Decl& decl = build.make_concept(n, ps); // declare(cxt, current_scope(), decl); // return decl; }
Decl& Parser::on_super_declaration(Name& n, Type& t) { lingo_unreachable(); // Decl& d = cxt.make_super_declaration(t); // declare(cxt, d); // return d; }
void instantiate_directive(Parser& p) { p.require("instantiate"); // Name& n = p.template_id(); // Actually show the instantated definition. lingo_unreachable(); }
Def& Parser::on_concept_definition(Decl& decl, Req_list& ds) { lingo_unreachable(); // Def& def = build.make_concept_definition(ds); // define_concept(decl, def); // return def; }
Def& Parser::on_concept_definition(Decl& decl, Expr& e) { lingo_unreachable(); // Def& def = build.make_expression_definition(e); // define_concept(decl, def); // return def; }
// Update `d` with the initializer `e`. static inline void initialize_declaration(Decl& d, Expr& e) { if (Variable_decl* var = as<Variable_decl>(&d)) var->init = &e; else lingo_unreachable(); }
Decl& Parser::on_type_template_parameter(Name& n, Type& t) { lingo_unreachable(); // Decl& parm = build.make_type_parameter(n, t); // declare(cxt, current_scope(), parm); // return parm; }
Expr& Parser::subscript_expression(Expr& e) { require(tk::lbracket_tok); // Expr& index = expression(); match(tk::rbracket_tok); lingo_unreachable(); }
// FIXME: This is dumb. We should have a base class that contributes // a type to the declaration hiearchy (Typed_decl). static inline Type& declared_type(Decl& d) { if (Variable_decl* var = as<Variable_decl>(&d)) return var->type(); // We can initialize other things too. lingo_unreachable(); }
// Perform qualified lookup to resolve the declaration referred to by obj.n. Expr& make_member_reference(Context& cxt, Expr& obj, Simple_id& name) { Type& type = obj.type(); Decl_list decls = qualified_lookup(cxt, type, name); if (decls.size() == 1) return make_member_reference(cxt, obj, decls.front()); lingo_unreachable(); }
void resolve_directive(Parser& p) { p.require("reolve"); Expr& e = p.postfix_expression(); std::cout << e << '\n'; // TODO: Actually show which function was resolved. lingo_unreachable(); }
std::ostream& operator<<(std::ostream& os, Validation v) { switch (v) { case valid_proof: return os << "valid"; case invalid_proof: return os << "invalid"; case incomplete_proof: return os << "incomplete"; default: lingo_unreachable(); } }
Expr const* Parser::on_unary(Token tok, Expr const* e) { Location loc = tok.location(); switch (tok.kind()) { case plus_tok: return new Pos(loc, e); case minus_tok: return new Neg(loc, e); default: break; } lingo_unreachable("invalid unary operator", tok.spelling()); }
// FIXME: How do I compare two templates with the same name? // Just give them different names? void order_template_directive(Parser& p) { // p.require(template_tok); // Name& n1 = p.template_id(); // Name& n2 = p.template_id(); // TODO: Resolve the function templates referred to by these // ids and determine which is more specialized. lingo_unreachable(); }
char const* get_spelling(Unary_op op) { switch (op) { case num_neg_op: return "-"; case num_pos_op: return "+"; case bit_not_op: return "~"; case log_not_op: return "!"; } lingo_unreachable("invalid unary operator ({})", (int)op); }
void inspect_directive(Parser& p) { p.require("inspect"); p.match(dot_tok); if (p.next_token_is("expression")) return inspect_expression_directive(p); // FIXME: Be a little more polite. lingo_unreachable(); }
// Synthesize a unique type, value, or template from a corresponding // template parameter. // // TODO: Handle template parameter packs. Term& synthesize_template_argument(Context& cxt, Decl& parm) { if (Type_parm* t = as<Type_parm>(&parm)) return synthesize_template_argument(cxt, *t); if (Value_parm* e = as<Value_parm>(&parm)) return synthesize_template_argument(cxt, *e); if (Template_parm* x = as<Template_parm>(&parm)) return synthesize_template_argument(cxt, *x); lingo_unreachable(); }
Term& substitute(Context& cxt, Term& x, Substitution& sub) { if (Type* t = as<Type>(&x)) return substitute(cxt, *t, sub); if (Expr* e = as<Expr>(&x)) return substitute(cxt, *e, sub); if (Decl* d = as<Decl>(&x)) return substitute(cxt, *d, sub); lingo_unreachable(); }