コード例 #1
0
ファイル: ast_walk_spec.c プロジェクト: helino/jurov
static void on_add(mj_binary_operation *op, void *result)
{
    int left, right = 0;
    ast_visit(op->left_operand, &left);
    ast_visit(op->right_operand, &right);
    *((int *) result) = left + right;
}
コード例 #2
0
ファイル: call.c プロジェクト: abingham/ponyc
static bool apply_default_arg(pass_opt_t* opt, ast_t* param, ast_t* arg)
{
  // Pick up a default argument.
  ast_t* def_arg = ast_childidx(param, 2);

  if(ast_id(def_arg) == TK_NONE)
  {
    ast_error(arg, "not enough arguments");
    return false;
  }

  ast_setid(arg, TK_SEQ);
  ast_add(arg, def_arg);

  // Type check the arg.
  if(ast_type(def_arg) == NULL)
  {
    if(ast_visit(&arg, NULL, pass_expr, opt) != AST_OK)
      return false;

    ast_visit(&arg, NULL, pass_nodebug, opt);
  } else {
    if(!expr_seq(arg))
      return false;
  }

  return true;
}
コード例 #3
0
ファイル: pass.c プロジェクト: jonas-l/ponyc
ast_result_t ast_visit_scope(ast_t** ast, ast_visit_t pre, ast_visit_t post,
  pass_opt_t* options, pass_id pass)
{
  typecheck_t* t = &options->check;
  bool pop = frame_push(t, NULL);

  ast_result_t ret = ast_visit(ast, pre, post, options, pass);

  if(pop)
    frame_pop(t);

  return ret;
}
コード例 #4
0
ファイル: traits.c プロジェクト: lzpfmh/ponyc
// Determine which body to use for the given method
static bool resolve_body(ast_t* entity, ast_t* method, pass_opt_t* options)
{
  assert(entity != NULL);
  assert(method != NULL);

  method_t* info = (method_t*)ast_data(method);
  assert(info != NULL);

  if(info->local_def) // Local defs just use their own body
    return true;

  token_id e_id = ast_id(entity);
  bool concrete =
    (e_id == TK_PRIMITIVE) || (e_id == TK_STRUCT) ||
    (e_id == TK_CLASS) || (e_id == TK_ACTOR);

  const char* name = ast_name(ast_childidx(method, 1));
  assert(name != NULL);

  // NExt try a delegate body
  ast_t* r = resolve_delegate_body(entity, method, info, name);

  if(r == BODY_ERROR)
    return false;

  if(r == NULL) // And finally a default body
    r = resolve_default_body(entity, method, info, name, concrete);

  if(r == BODY_ERROR)
    return false;

  if(r != NULL)
  {
    // We have a body, use it and patch up symbol tables
    ast_t* old_body = ast_childidx(method, 6);
    ast_replace(&old_body, r);
    ast_visit(&method, rescope, NULL, options, PASS_ALL);
    return true;
  }

  // Nowhere left to get a body from
  if(concrete)
  {
    ast_error(entity, "no body found for method %s", name);
    ast_error(method, "provided from here");
    return false;
  }

  return true;
}
コード例 #5
0
ファイル: pass.c プロジェクト: jonas-l/ponyc
bool module_passes(ast_t* package, pass_opt_t* options, source_t* source)
{
  if(!pass_parse(package, source))
    return false;

  if(options->limit < PASS_SYNTAX)
    return true;

  ast_t* module = ast_child(package);

  if(ast_visit(&module, pass_syntax, NULL, options, PASS_SYNTAX) != AST_OK)
    return false;

  check_tree(module);
  return true;
}
コード例 #6
0
ファイル: pass.c プロジェクト: ozra/ponyc
// Do a single pass, if the limit allows
static bool do_pass(ast_t** astp, bool* out_result, pass_opt_t* options,
  pass_id pass, ast_visit_t pre_fn, ast_visit_t post_fn)
{
  if(options->limit < pass)
  {
    *out_result = true;
    return true;
  }

  if(ast_visit(astp, pre_fn, post_fn, options) != AST_OK)
  {
    *out_result = false;
    return true;
  }

  return false;
}
コード例 #7
0
ファイル: pass.c プロジェクト: federicosan/ponyc
ast_result_t ast_visit_scope(ast_t** ast, ast_visit_t pre, ast_visit_t post,
  pass_opt_t* options, pass_id pass)
{
  typecheck_t* t = &options->check;
  ast_t* module = ast_nearest(*ast, TK_MODULE);
  ast_t* package = ast_parent(module);
  assert(module != NULL);
  assert(package != NULL);

  frame_push(t, NULL);
  frame_push(t, package);
  frame_push(t, module);

  ast_result_t ret = ast_visit(ast, pre, post, options, pass);

  frame_pop(t);
  frame_pop(t);
  frame_pop(t);

  return ret;
}
コード例 #8
0
ファイル: pass.c プロジェクト: federicosan/ponyc
// Perform an ast_visit pass, after checking the pass limits.
// Returns true to continue, false to stop processing and return the value in
// out_r.
static bool visit_pass(ast_t** astp, pass_opt_t* options, pass_id last_pass,
  bool* out_r, pass_id pass, ast_visit_t pre_fn, ast_visit_t post_fn)
{
  assert(out_r != NULL);

  if(!check_limit(astp, options, pass, last_pass))
  {
    *out_r = true;
    return false;
  }

  //fprintf(stderr, "Pass %s (last %s) on %s\n", pass_name(pass),
  //  pass_name(last_pass), ast_get_print(*astp));

  if(ast_visit(astp, pre_fn, post_fn, options, pass) != AST_OK)
  {
    *out_r = false;
    return false;
  }

  return true;
}
コード例 #9
0
ファイル: sugar.c プロジェクト: shepheb/ponyc
static ast_result_t sugar_as(pass_opt_t* opt, ast_t** astp)
{
  typecheck_t* t = &opt->check;
  ast_t* ast = *astp;
  AST_GET_CHILDREN(ast, expr, type);

  ast_t* pattern_root = ast_from(type, TK_LEX_ERROR);
  ast_t* body_root = ast_from(type, TK_LEX_ERROR);
  add_as_type(t, type, pattern_root, body_root);

  ast_t* body = ast_pop(body_root);
  ast_free(body_root);

  if(body == NULL)
  {
    // No body implies all types are "don't care"
    ast_error(ast, "Cannot treat value as \"don't care\"");
    ast_free(pattern_root);
    return AST_ERROR;
  }

  // Don't need top sequence in pattern
  assert(ast_id(ast_child(pattern_root)) == TK_SEQ);
  ast_t* pattern = ast_pop(ast_child(pattern_root));
  ast_free(pattern_root);

  REPLACE(astp,
    NODE(TK_MATCH, AST_SCOPE
      NODE(TK_SEQ, TREE(expr))
      NODE(TK_CASES, AST_SCOPE
        NODE(TK_CASE, AST_SCOPE
          TREE(pattern)
          NONE
          TREE(body)))
      NODE(TK_SEQ, AST_SCOPE NODE(TK_ERROR, NONE))));

  return ast_visit(astp, pass_sugar, NULL, opt, PASS_SUGAR);
}
コード例 #10
0
ファイル: lambda.c プロジェクト: Praetonus/ponyc
bool expr_lambda(pass_opt_t* opt, ast_t** astp)
{
  assert(astp != NULL);
  ast_t* ast = *astp;
  assert(ast != NULL);

  AST_GET_CHILDREN(ast, cap, name, t_params, params, captures, ret_type,
    raises, body);

  ast_t* members = ast_from(ast, TK_MEMBERS);
  ast_t* last_member = NULL;
  bool failed = false;

  // Process captures
  for(ast_t* p = ast_child(captures); p != NULL; p = ast_sibling(p))
  {
    ast_t* field = make_capture_field(opt, p);

    if(field != NULL)
      ast_list_append(members, &last_member, field);
    else  // An error occurred, just keep going to potentially find more errors
      failed = true;
  }

  if(failed)
  {
    ast_free(members);
    return false;
  }

  // Stop the various elements being marked as preserve
  ast_clearflag(t_params, AST_FLAG_PRESERVE);
  ast_clearflag(params, AST_FLAG_PRESERVE);
  ast_clearflag(ret_type, AST_FLAG_PRESERVE);
  ast_clearflag(body, AST_FLAG_PRESERVE);

  const char* fn_name = "apply";

  if(ast_id(name) == TK_ID)
    fn_name = ast_name(name);

  // Make the apply function
  BUILD(apply, ast,
    NODE(TK_FUN, AST_SCOPE
      TREE(cap)
      ID(fn_name)
      TREE(t_params)
      TREE(params)
      TREE(ret_type)
      TREE(raises)
      TREE(body)
      NONE    // Doc string
      NONE)); // Guard

  ast_list_append(members, &last_member, apply);

  printbuf_t* buf = printbuf_new();
  printbuf(buf, "lambda(");
  bool first = true;

  for(ast_t* p = ast_child(params); p != NULL; p = ast_sibling(p))
  {
    if(first)
      first = false;
    else
      printbuf(buf, ", ");

    printbuf(buf, "%s", ast_print_type(ast_childidx(p, 1)));
  }

  printbuf(buf, ")");

  if(ast_id(ret_type) != TK_NONE)
    printbuf(buf, ": %s", ast_print_type(ret_type));

  if(ast_id(raises) != TK_NONE)
    printbuf(buf, " ?");

  printbuf(buf, " end");

  // Replace lambda with object literal
  REPLACE(astp,
    NODE(TK_OBJECT, DATA(stringtab(buf->m))
      NONE
      NONE  // Provides list
      TREE(members)));

  printbuf_free(buf);

  // Catch up passes
  if(ast_visit(astp, pass_syntax, NULL, opt, PASS_SYNTAX) != AST_OK)
    return false;

  return ast_passes_subtree(astp, opt, PASS_EXPR);
}
コード例 #11
0
ファイル: traits.c プロジェクト: abingham/ponyc
// Check resulting methods are compatible with the containing entity and patch
// up symbol tables
static bool post_process_methods(ast_t* entity, pass_opt_t* options,
  bool is_concrete)
{
  assert(entity != NULL);

  bool r = true;
  ast_t* members = ast_childidx(entity, 4);

  for(ast_t* m = ast_child(members); m != NULL; m = ast_sibling(m))
  {
    token_id variety = ast_id(m);

    // Check behaviour compatability
    if(variety == TK_BE)
    {
      switch(ast_id(entity))
      {
        case TK_PRIMITIVE:
          ast_error(entity,
            "primitives can't provide traits that have behaviours");
          r = false;
          break;

        case TK_CLASS:
          ast_error(entity, "classes can't have provide that have behaviours");
          r = false;
          break;

        default:
          break;
      }
    }

    if(variety == TK_BE || variety == TK_FUN || variety == TK_NEW)
    {
      // Check concrete method bodies
      if(ast_data(m) == BODY_AMBIGUOUS)
      {
        if(is_concrete)
        {
          ast_error(m, "multiple possible method bodies from traits");
          r = false;
        }
      }
      else if(ast_data(m) == NULL)
      {
        if(is_concrete)
        {
          assert(ast_id(ast_childidx(m, 6)) == TK_NONE);
          ast_error(m, "no body found for method %d %s %s", is_concrete,
            ast_get_print(entity), ast_name(ast_child(entity)));
          r = false;
        }
      }
      else
      {
        assert(ast_id(ast_childidx(m, 6)) != TK_NONE);

        if(ast_data(m) != entity)
        {
          // Sort out copied symbol tables
          ast_visit(&m, rescope, NULL, options);
        }
      }
    }
  }

  return r;
}
コード例 #12
0
ファイル: traits.c プロジェクト: awaidmann/ponyc
// Combine the given inherited method with the existing one, if any, in the
// given entity.
// The provided method must already be reified.
// The trait_ref is the entry in the provides list that causes this method
// inclusion. Needed for error reporting.
// Returns true on success, false on failure in which case an error will have
// been reported.
static bool add_method_from_trait(ast_t* entity, ast_t* method,
  ast_t* trait_ref, pass_opt_t* opt)
{
  assert(entity != NULL);
  assert(method != NULL);
  assert(trait_ref != NULL);

  AST_GET_CHILDREN(method, cap, id, t_params, params, result, error,
    method_body);

  const char* method_name = ast_name(id);
  ast_t* existing_method = find_method(entity, method_name);

  if(existing_method == NULL)
  {
    // We don't have a method yet with this name, add the one from this trait.
    ast_t* m = add_method(entity, trait_ref, method, "provided", opt);

    if(m == NULL)
      return false;

    if(ast_id(ast_childidx(m, 6)) != TK_NONE)
      ast_visit(&m, rescope, NULL, opt, PASS_ALL);

    return true;
  }

  // A method with this name already exists.
  method_t* info = (method_t*)ast_data(existing_method);
  assert(info != NULL);

  // Method has already caused an error, do nothing.
  if(info->failed)
    return false;

  if(info->local_define || info->delegated_field != NULL)
    return true;

  // Existing method is also provided, signatures must match exactly.
  if(!compare_signatures(existing_method, method))
  {
    assert(info->trait_ref != NULL);

    ast_error(opt->check.errors, trait_ref,
      "clashing definitions for method '%s' provided, local disambiguation "
      "required",
      method_name);
    ast_error_continue(opt->check.errors, trait_ref,
      "provided here, type: %s",
      ast_print_type(method));
    ast_error_continue(opt->check.errors, info->trait_ref,
      "and here, type: %s",
      ast_print_type(existing_method));

    info->failed = true;
    return false;
  }

  // Resolve bodies, if any.
  ast_t* existing_body = ast_childidx(existing_method, 6);

  bool multiple_bodies =
    (info->body_donor != NULL) &&
    (ast_id(method_body) != TK_NONE) &&
    (info->body_donor != (ast_t*)ast_data(method));

  if(multiple_bodies ||
    ast_checkflag(existing_method, AST_FLAG_AMBIGUOUS) ||
    ast_checkflag(method, AST_FLAG_AMBIGUOUS))
  {
    // This method body ambiguous, which is not necessarily an error.
    ast_setflag(existing_method, AST_FLAG_AMBIGUOUS);

    if(ast_id(existing_body) != TK_NONE) // Ditch existing body.
      ast_replace(&existing_body, ast_from(existing_method, TK_NONE));

    info->body_donor = NULL;
    return true;
  }

  // No new body to resolve.
  if((ast_id(method_body) == TK_NONE) ||
    (info->body_donor == (ast_t*)ast_data(method)))
    return true;

  // Trait provides default body. Use it and patch up symbol tables.
  assert(ast_id(existing_body) == TK_NONE);
  ast_replace(&existing_body, method_body);
  ast_visit(&method_body, rescope, NULL, opt, PASS_ALL);

  info->body_donor = (ast_t*)ast_data(method);
  info->trait_ref = trait_ref;
  return true;
}
コード例 #13
0
ファイル: pass.c プロジェクト: federicosan/ponyc
ast_result_t ast_visit(ast_t** ast, ast_visit_t pre, ast_visit_t post,
  pass_opt_t* options, pass_id pass)
{
  assert(ast != NULL);
  assert(*ast != NULL);

  pass_id ast_pass = (pass_id)ast_checkflag(*ast, AST_FLAG_PASS_MASK);

  if(ast_pass >= pass)  // This pass already done for this AST node
    return AST_OK;

  if(ast_checkflag(*ast, AST_FLAG_PRESERVE))  // Do not process this subtree
    return AST_OK;

  typecheck_t* t = &options->check;
  bool pop = frame_push(t, *ast);

  ast_result_t ret = AST_OK;
  bool ignore = false;

  if(pre != NULL)
  {
    switch(pre(ast, options))
    {
      case AST_OK:
        break;

      case AST_IGNORE:
        ignore = true;
        break;

      case AST_ERROR:
        ret = AST_ERROR;
        break;

      case AST_FATAL:
        record_ast_pass(*ast, pass);
        return AST_FATAL;
    }
  }

  if(!ignore && ((pre != NULL) || (post != NULL)))
  {
    ast_t* child = ast_child(*ast);

    while(child != NULL)
    {
      switch(ast_visit(&child, pre, post, options, pass))
      {
        case AST_OK:
          break;

        case AST_IGNORE:
          // Can never happen
          assert(0);
          break;

        case AST_ERROR:
          ret = AST_ERROR;
          break;

        case AST_FATAL:
          record_ast_pass(*ast, pass);
          return AST_FATAL;
      }

      child = ast_sibling(child);
    }
  }

  if(!ignore && post != NULL)
  {
    switch(post(ast, options))
    {
      case AST_OK:
      case AST_IGNORE:
        break;

      case AST_ERROR:
        ret = AST_ERROR;
        break;

      case AST_FATAL:
        record_ast_pass(*ast, pass);
        return AST_FATAL;
    }
  }

  if(pop)
    frame_pop(t);

  record_ast_pass(*ast, pass);
  return ret;
}
コード例 #14
0
ファイル: lambda.c プロジェクト: Perelandric/ponyc
bool expr_object(pass_opt_t* opt, ast_t** astp)
{
  ast_t* ast = *astp;
  bool ok = true;

  AST_GET_CHILDREN(ast, cap, provides, members);
  ast_clearflag(cap, AST_FLAG_PRESERVE);
  ast_clearflag(provides, AST_FLAG_PRESERVE);
  ast_clearflag(members, AST_FLAG_PRESERVE);

  ast_t* annotation = ast_consumeannotation(ast);
  const char* c_id = package_hygienic_id(&opt->check);

  ast_t* t_params;
  ast_t* t_args;
  collect_type_params(ast, &t_params, &t_args);

  const char* nice_id = (const char*)ast_data(ast);

  if(nice_id == NULL)
    nice_id = "object literal";

  // Create a new anonymous type.
  BUILD(def, ast,
    NODE(TK_CLASS, AST_SCOPE
      ANNOTATE(annotation)
      NICE_ID(c_id, nice_id)
      TREE(t_params)
      NONE
      TREE(provides)
      NODE(TK_MEMBERS)
      NONE
      NONE));

  // We will have a create method in the type.
  BUILD(create, members,
    NODE(TK_NEW, AST_SCOPE
      NONE
      ID("create")
      NONE
      NODE(TK_PARAMS)
      NONE
      NONE
      NODE(TK_SEQ,
        NODE(TK_TRUE))
      NONE
      NONE));

  BUILD(type_ref, ast, NODE(TK_REFERENCE, ID(c_id)));

  if(ast_id(t_args) != TK_NONE)
  {
    // Need to add type args to our type reference
    BUILD(t, ast, NODE(TK_QUALIFY, TREE(type_ref) TREE(t_args)));
    type_ref = t;
  }

  ast_free_unattached(t_args);

  // We will replace object..end with $0.create(...)
  BUILD(call, ast,
    NODE(TK_CALL,
      NODE(TK_POSITIONALARGS)
      NONE
      NONE
      NODE(TK_DOT,
        TREE(type_ref)
        ID("create"))));

  ast_t* create_params = ast_childidx(create, 3);
  ast_t* create_body = ast_childidx(create, 6);
  ast_t* call_args = ast_child(call);
  ast_t* class_members = ast_childidx(def, 4);
  ast_t* member = ast_child(members);

  bool has_fields = false;
  bool has_behaviours = false;

  while(member != NULL)
  {
    switch(ast_id(member))
    {
      case TK_FVAR:
      case TK_FLET:
      case TK_EMBED:
      {
        add_field_to_object(opt, member, class_members, create_params,
          create_body, call_args);

        has_fields = true;
        break;
      }

      case TK_BE:
        // If we have behaviours, we must be an actor.
        ast_append(class_members, member);
        has_behaviours = true;
        break;

      default:
        // Keep all the methods as they are.
        ast_append(class_members, member);
        break;
    }

    member = ast_sibling(member);
  }

  // Add the create function at the end.
  ast_append(class_members, create);

  // Add new type to current module and bring it up to date with passes.
  ast_t* module = ast_nearest(ast, TK_MODULE);
  ast_append(module, def);

  // Turn any free variables into fields.
  ast_t* captures = ast_from(ast, TK_MEMBERS);
  ast_t* last_capture = NULL;

  if(!capture_from_type(opt, *astp, &def, captures, &last_capture))
    ok = false;

  for(ast_t* p = ast_child(captures); p != NULL; p = ast_sibling(p))
  {
    add_field_to_object(opt, p, class_members, create_params, create_body,
      call_args);
    has_fields = true;
  }

  ast_free_unattached(captures);
  ast_resetpass(def, PASS_SUGAR);

  // Handle capability and whether the anonymous type is a class, primitive or
  // actor.
  token_id cap_id = ast_id(cap);

  if(has_behaviours)
  {
    // Change the type to an actor.
    ast_setid(def, TK_ACTOR);

    if(cap_id != TK_NONE && cap_id != TK_TAG)
    {
      ast_error(opt->check.errors, cap, "object literals with behaviours are "
        "actors and so must have tag capability");
      ok = false;
    }

    cap_id = TK_TAG;
  }
  else if(!has_fields && (cap_id == TK_NONE || cap_id == TK_TAG ||
    cap_id == TK_BOX || cap_id == TK_VAL))
  {
    // Change the type from a class to a primitive.
    ast_setid(def, TK_PRIMITIVE);
    cap_id = TK_VAL;
  }

  if(ast_id(def) != TK_PRIMITIVE)
    pony_assert(!ast_has_annotation(def, "ponyint_bare"));

  // Reset constructor to pick up the correct defaults.
  ast_setid(ast_child(create), cap_id);
  ast_t* result = ast_childidx(create, 4);
  ast_replace(&result,
    type_for_class(opt, def, result, cap_id, TK_EPHEMERAL, false));

  // Catch up provides before catching up the entire type.
  if(!catch_up_provides(opt, provides))
    return false;

  // Type check the anonymous type.
  if(!ast_passes_type(&def, opt, PASS_EXPR))
    return false;

  // Replace object..end with $0.create(...)
  ast_replace(astp, call);

  if(ast_visit(astp, pass_syntax, NULL, opt, PASS_SYNTAX) != AST_OK)
    return false;

  if(!ast_passes_subtree(astp, opt, PASS_EXPR))
    return false;

  return ok;
}
コード例 #15
0
ファイル: lambda.c プロジェクト: Perelandric/ponyc
bool expr_lambda(pass_opt_t* opt, ast_t** astp)
{
  pony_assert(astp != NULL);
  ast_t* ast = *astp;
  pony_assert(ast != NULL);

  AST_GET_CHILDREN(ast, receiver_cap, name, t_params, params, captures,
    ret_type, raises, body, reference_cap);
  ast_t* annotation = ast_consumeannotation(ast);

  bool bare = ast_id(ast) == TK_BARELAMBDA;
  ast_t* members = ast_from(ast, TK_MEMBERS);
  ast_t* last_member = NULL;
  bool failed = false;

  if(bare)
    pony_assert(ast_id(captures) == TK_NONE);

  // Process captures
  for(ast_t* p = ast_child(captures); p != NULL; p = ast_sibling(p))
  {
    ast_t* field = NULL;
    bool ok = make_capture_field(opt, p, &field);

    if(field != NULL)
      ast_list_append(members, &last_member, field);
    else if(!ok)
      // An error occurred, just keep going to potentially find more errors
      failed = true;
  }

  if(failed)
  {
    ast_free(members);
    return false;
  }

  // Stop the various elements being marked as preserve
  ast_clearflag(t_params, AST_FLAG_PRESERVE);
  ast_clearflag(params, AST_FLAG_PRESERVE);
  ast_clearflag(ret_type, AST_FLAG_PRESERVE);
  ast_clearflag(body, AST_FLAG_PRESERVE);

  const char* fn_name = "apply";

  if(ast_id(name) == TK_ID)
    fn_name = ast_name(name);

  // Make the apply function
  BUILD(apply, ast,
    NODE(TK_FUN, AST_SCOPE
      ANNOTATE(annotation)
      TREE(receiver_cap)
      ID(fn_name)
      TREE(t_params)
      TREE(params)
      TREE(ret_type)
      TREE(raises)
      TREE(body)
      NONE    // Doc string
      NONE)); // Guard

  ast_list_append(members, &last_member, apply);
  ast_setflag(members, AST_FLAG_PRESERVE);

  printbuf_t* buf = printbuf_new();
  printbuf(buf, bare ? "@{(" : "{(");
  bool first = true;

  for(ast_t* p = ast_child(params); p != NULL; p = ast_sibling(p))
  {
    if(first)
      first = false;
    else
      printbuf(buf, ", ");

    printbuf(buf, "%s", ast_print_type(ast_childidx(p, 1)));
  }

  printbuf(buf, ")");

  if(ast_id(ret_type) != TK_NONE)
    printbuf(buf, ": %s", ast_print_type(ret_type));

  if(ast_id(raises) != TK_NONE)
    printbuf(buf, " ?");

  printbuf(buf, "}");

  // Replace lambda with object literal
  REPLACE(astp,
    NODE(TK_OBJECT, DATA(stringtab(buf->m))
      TREE(reference_cap)
      NONE  // Provides list
      TREE(members)));

  printbuf_free(buf);

  if(bare)
  {
    BUILD(bare_annotation, *astp,
      NODE(TK_ANNOTATION,
        ID("ponyint_bare")));

    // Record the syntax pass as done to avoid the error about internal
    // annotations.
    ast_pass_record(bare_annotation, PASS_SYNTAX);
    ast_setannotation(*astp, bare_annotation);
  }

  // Catch up passes
  if(ast_visit(astp, pass_syntax, NULL, opt, PASS_SYNTAX) != AST_OK)
    return false;

  return ast_passes_subtree(astp, opt, PASS_EXPR);
}
コード例 #16
0
ファイル: lambda.c プロジェクト: lispmeister/ponyc
bool expr_lambda(pass_opt_t* opt, ast_t** astp)
{
  pony_assert(astp != NULL);
  ast_t* ast = *astp;
  pony_assert(ast != NULL);

  AST_GET_CHILDREN(ast, receiver_cap, name, t_params, params, captures,
    ret_type, raises, body, obj_cap);
  ast_t* annotation = ast_consumeannotation(ast);

  // Try to find an antecedent type, and find possible lambda interfaces in it.
  ast_t* antecedent_type = find_antecedent_type(opt, ast, NULL);
  astlist_t* possible_fun_defs = NULL;
  astlist_t* possible_obj_caps = NULL;
  if(!is_typecheck_error(antecedent_type))
    find_possible_fun_defs(opt, antecedent_type, &possible_fun_defs,
      &possible_obj_caps);

  // If there's more than one possible fun defs, rule out impossible ones by
  // comparing each fun def by some basic criteria against the lambda,
  // creating a new list containing only the remaining possibilities.
  if(astlist_length(possible_fun_defs) > 1)
  {
    astlist_t* new_fun_defs = NULL;
    astlist_t* new_obj_caps = NULL;

    astlist_t* fun_def_cursor = possible_fun_defs;
    astlist_t* obj_cap_cursor = possible_obj_caps;
    for(; (fun_def_cursor != NULL) && (obj_cap_cursor != NULL);
      fun_def_cursor = astlist_next(fun_def_cursor),
      obj_cap_cursor = astlist_next(obj_cap_cursor))
    {
      ast_t* fun_def = astlist_data(fun_def_cursor);
      ast_t* def_obj_cap = astlist_data(obj_cap_cursor);

      if(is_typecheck_error(fun_def))
        continue;

      AST_GET_CHILDREN(fun_def, def_receiver_cap, def_name, def_t_params,
        def_params, def_ret_type, def_raises);

      // Must have the same number of parameters.
      if(ast_childcount(params) != ast_childcount(def_params))
        continue;

      // Must have a supercap of the def's receiver cap (if present).
      if((ast_id(receiver_cap) != TK_NONE) && !is_cap_sub_cap(
        ast_id(def_receiver_cap), TK_NONE, ast_id(receiver_cap), TK_NONE)
        )
        continue;

      // Must have a supercap of the def's object cap (if present).
      if((ast_id(obj_cap) != TK_NONE) &&
        !is_cap_sub_cap(ast_id(obj_cap), TK_NONE, ast_id(def_obj_cap), TK_NONE))
        continue;

      // TODO: This logic could potentially be expanded to do deeper
      // compatibility checks, but checks involving subtyping here would be
      // difficult, because the lambda's AST is not caught up yet in the passes.

      new_fun_defs = astlist_push(new_fun_defs, fun_def);
      new_obj_caps = astlist_push(new_obj_caps, def_obj_cap);
    }

    astlist_free(possible_fun_defs);
    astlist_free(possible_obj_caps);
    possible_fun_defs = new_fun_defs;
    possible_obj_caps = new_obj_caps;
  }

  if(astlist_length(possible_fun_defs) == 1)
  {
    ast_t* fun_def = astlist_data(possible_fun_defs);
    ast_t* def_obj_cap = astlist_data(possible_obj_caps);

    // Try to complete the lambda's type info by inferring from the lambda type.
    if(!is_typecheck_error(fun_def))
    {
      // Infer the object cap, receiver cap, and return type if unspecified.
      if(ast_id(obj_cap) == TK_NONE)
        ast_replace(&obj_cap, def_obj_cap);
      if(ast_id(receiver_cap) == TK_NONE)
        ast_replace(&receiver_cap, ast_child(fun_def));
      if(ast_id(ret_type) == TK_NONE)
        ast_replace(&ret_type, ast_childidx(fun_def, 4));

      // Infer the type of any parameters that were left unspecified.
      ast_t* param = ast_child(params);
      ast_t* def_param = ast_child(ast_childidx(fun_def, 3));
      while((param != NULL) && (def_param != NULL))
      {
        ast_t* param_id = ast_child(param);
        ast_t* param_type = ast_sibling(param_id);

        // Convert a "_" parameter to whatever the expected parameter is.
        if(is_name_dontcare(ast_name(param_id)))
        {
          ast_replace(&param_id, ast_child(def_param));
          ast_replace(&param_type, ast_childidx(def_param, 1));
        }
        // Give a type-unspecified parameter the type of the expected parameter.
        else if(ast_id(param_type) == TK_NONE)
        {
          ast_replace(&param_type, ast_childidx(def_param, 1));
        }

        param = ast_sibling(param);
        def_param = ast_sibling(def_param);
      }
    }

    ast_free_unattached(fun_def);
  }

  astlist_free(possible_obj_caps);

  // If any parameters still have no type specified, it's an error.
  ast_t* param = ast_child(params);
  while(param != NULL)
  {
    if(ast_id(ast_childidx(param, 1)) == TK_NONE)
    {
      ast_error(opt->check.errors, param,
        "a lambda parameter must specify a type or be inferable from context");

      if(astlist_length(possible_fun_defs) > 1)
      {
        for(astlist_t* fun_def_cursor = possible_fun_defs;
          fun_def_cursor != NULL;
          fun_def_cursor = astlist_next(fun_def_cursor))
        {
          ast_error_continue(opt->check.errors, astlist_data(fun_def_cursor),
            "this lambda interface is inferred, but it's not the only one");
        }
      }

      astlist_free(possible_fun_defs);
      return false;
    }
    param = ast_sibling(param);
  }

  astlist_free(possible_fun_defs);

  bool bare = ast_id(ast) == TK_BARELAMBDA;
  ast_t* members = ast_from(ast, TK_MEMBERS);
  ast_t* last_member = NULL;
  bool failed = false;

  if(bare)
    pony_assert(ast_id(captures) == TK_NONE);

  // Process captures
  for(ast_t* p = ast_child(captures); p != NULL; p = ast_sibling(p))
  {
    ast_t* field = NULL;
    bool ok = make_capture_field(opt, p, &field);

    if(field != NULL)
      ast_list_append(members, &last_member, field);
    else if(!ok)
      // An error occurred, just keep going to potentially find more errors
      failed = true;
  }

  if(failed)
  {
    ast_free(members);
    return false;
  }

  // Stop the various elements being marked as preserve
  ast_clearflag(t_params, AST_FLAG_PRESERVE);
  ast_clearflag(params, AST_FLAG_PRESERVE);
  ast_clearflag(ret_type, AST_FLAG_PRESERVE);
  ast_clearflag(body, AST_FLAG_PRESERVE);

  const char* fn_name = "apply";

  if(ast_id(name) == TK_ID)
    fn_name = ast_name(name);

  // Make the apply function
  BUILD(apply, ast,
    NODE(TK_FUN, AST_SCOPE
      ANNOTATE(annotation)
      TREE(receiver_cap)
      ID(fn_name)
      TREE(t_params)
      TREE(params)
      TREE(ret_type)
      TREE(raises)
      TREE(body)
      NONE)); // Doc string

  ast_list_append(members, &last_member, apply);
  ast_setflag(members, AST_FLAG_PRESERVE);

  printbuf_t* buf = printbuf_new();
  printbuf(buf, bare ? "@{(" : "{(");
  bool first = true;

  for(ast_t* p = ast_child(params); p != NULL; p = ast_sibling(p))
  {
    if(first)
      first = false;
    else
      printbuf(buf, ", ");

    printbuf(buf, "%s", ast_print_type(ast_childidx(p, 1)));
  }

  printbuf(buf, ")");

  if(ast_id(ret_type) != TK_NONE)
    printbuf(buf, ": %s", ast_print_type(ret_type));

  if(ast_id(raises) != TK_NONE)
    printbuf(buf, " ?");

  printbuf(buf, "}");

  // Replace lambda with object literal
  REPLACE(astp,
    NODE(TK_OBJECT, DATA(stringtab(buf->m))
      TREE(obj_cap)
      NONE  // Provides list
      TREE(members)));

  printbuf_free(buf);

  if(bare)
  {
    BUILD(bare_annotation, *astp,
      NODE(TK_ANNOTATION,
        ID("ponyint_bare")));

    // Record the syntax pass as done to avoid the error about internal
    // annotations.
    ast_pass_record(bare_annotation, PASS_SYNTAX);
    ast_setannotation(*astp, bare_annotation);
  }

  // Catch up passes
  if(ast_visit(astp, pass_syntax, NULL, opt, PASS_SYNTAX) != AST_OK)
    return false;

  return ast_passes_subtree(astp, opt, PASS_EXPR);
}