Esempio n. 1
0
static ast_result_t flatten_isect(pass_opt_t* opt, ast_t* ast)
{
  // Flatten intersections without testing subtyping. This is to preserve any
  // type guarantees that an element in the intersection might make.
  // If there are more than 2 children, this has already been flattened.
  if(ast_childcount(ast) > 2)
    return AST_OK;

  AST_EXTRACT_CHILDREN(ast, left, right);

  if((opt->check.frame->constraint == NULL) &&
    (opt->check.frame->iftype_constraint == NULL) &&
    (opt->check.frame->provides == NULL) &&
    !is_compat_type(left, right))
  {
    ast_add(ast, right);
    ast_add(ast, left);

    ast_error(opt->check.errors, ast,
      "intersection types cannot include reference capabilities that are not "
      "locally compatible");
    return AST_ERROR;
  }

  flatten_typeexpr_element(ast, left, TK_ISECTTYPE);
  flatten_typeexpr_element(ast, right, TK_ISECTTYPE);

  return AST_OK;
}
Esempio n. 2
0
static bool apply_default_arg(pass_opt_t* opt, ast_t* param, ast_t* arg)
{
  // Pick up a default argument.
  AST_GET_CHILDREN(param, id, type, def_arg);

  if(ast_id(def_arg) == TK_NONE)
  {
    ast_error(opt->check.errors, arg, "not enough arguments");
    return false;
  }

  if(ast_id(def_arg) == TK_LOCATION)
  {
    // Default argument is __loc. Expand call location.
    ast_t* location = expand_location(arg);
    ast_add(arg, location);

    if(!ast_passes_subtree(&location, opt, PASS_EXPR))
      return false;
  }
  else
  {
    // Just use default argument.
    ast_add(arg, def_arg);
  }

  ast_setid(arg, TK_SEQ);

  if(!expr_seq(opt, arg))
    return false;

  return true;
}
Esempio n. 3
0
static ast_t* make_single_arrow(ast_t* left, ast_t* right)
{
  switch(ast_id(left))
  {
    case TK_ARROW:
    {
      ast_t* arrow = ast_dup(left);
      ast_t* child = ast_childidx(arrow, 1);

      // Arrow is right associative.
      while(ast_id(child) == TK_ARROW)
        child = ast_childidx(child, 1);

      ast_t* view = viewpoint_type(child, right);

      if(view == NULL)
      {
        ast_free_unattached(arrow);
        return NULL;
      }

      ast_replace(&child, view);
      return arrow;
    }

    default: {}
  }

  ast_t* arrow = ast_from(left, TK_ARROW);
  ast_add(arrow, right);
  ast_add(arrow, left);
  return arrow;
}
Esempio n. 4
0
static ast_result_t sugar_with(typecheck_t* t, ast_t** astp)
{
  AST_EXTRACT_CHILDREN(*astp, withexpr, body, else_clause);
  token_id try_token;

  if(ast_id(else_clause) == TK_NONE)
    try_token = TK_TRY_NO_CHECK;
  else
    try_token = TK_TRY;

  expand_none(else_clause, false);

  // First build a skeleton try block without the "with" variables
  BUILD(replace, *astp,
    NODE(TK_SEQ,
      NODE(try_token,
        NODE(TK_SEQ, AST_SCOPE
          TREE(body))
        NODE(TK_SEQ, AST_SCOPE
          TREE(else_clause))
        NODE(TK_SEQ, AST_SCOPE))));

  ast_t* tryexpr = ast_child(replace);
  AST_GET_CHILDREN(tryexpr, try_body, try_else, try_then);

  // Add the "with" variables from each with element
  for(ast_t* p = ast_child(withexpr); p != NULL; p = ast_sibling(p))
  {
    assert(ast_id(p) == TK_SEQ);
    AST_GET_CHILDREN(p, idseq, init);
    const char* init_name = package_hygienic_id(t);

    BUILD(assign, idseq,
      NODE(TK_ASSIGN, AST_NODEBUG
        TREE(init)
        NODE(TK_LET, ID(init_name) NONE)));

    BUILD(local, idseq,
      NODE(TK_ASSIGN, AST_NODEBUG
        NODE(TK_REFERENCE, ID(init_name))
        TREE(idseq)));

    ast_add(replace, assign);
    ast_add(try_body, local);
    ast_add(try_else, local);
    build_with_dispose(try_then, idseq);
    ast_add(try_then, local);
  }

  ast_replace(astp, replace);
  return AST_OK;
}
Esempio n. 5
0
static void build_with_dispose(ast_t* dispose_clause, ast_t* idseq)
{
  assert(dispose_clause != NULL);
  assert(idseq != NULL);

  if(ast_id(idseq) == TK_LET)
  {
    // Just a single variable
    ast_t* id = ast_child(idseq);
    assert(id != NULL);

    // Don't call dispose() on don't cares
    if(ast_id(id) == TK_DONTCARE)
      return;

    assert(ast_id(id) == TK_ID);
    BUILD(dispose, idseq,
      NODE(TK_CALL,
        NONE NONE
        NODE(TK_DOT, NODE(TK_REFERENCE, TREE(id)) ID("dispose"))));

    ast_add(dispose_clause, dispose);
    return;
  }

  // We have a list of variables
  assert(ast_id(idseq) == TK_TUPLE);

  for(ast_t* p = ast_child(idseq); p != NULL; p = ast_sibling(p))
    build_with_dispose(dispose_clause, p);
}
Esempio n. 6
0
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;
}
Esempio n. 7
0
static bool insert_apply(pass_opt_t* opt, ast_t** astp)
{
  // Sugar .apply()
  ast_t* ast = *astp;
  AST_GET_CHILDREN(ast, positional, namedargs, lhs);

  ast_t* dot = ast_from(ast, TK_DOT);
  ast_add(dot, ast_from_string(ast, "apply"));
  ast_swap(lhs, dot);
  ast_add(dot, lhs);

  if(!expr_dot(opt, &dot))
    return false;

  return expr_call(opt, astp);
}
Esempio n. 8
0
ast_t* builder_add(builder_t* builder, const char* add_point,
  const char* description)
{
  assert(add_point != NULL);
  assert(description != NULL);

  if(builder == NULL)
    return NULL;

  ast_t* add_parent = builder_find_sub_tree(builder, add_point);

  if(add_parent == NULL)
  {
    error(NULL, 0, 0, "Node with attribute {def %s} not found", add_point);
    return NULL;
  }

  ast_t* ast = builder_add_ast(builder, description);

  if(ast == NULL)
    return NULL;

  // Success, add new tree to builder
  ast_add(add_parent, ast);
  return ast;
}
Esempio n. 9
0
static void add_field_to_object(pass_opt_t* opt, ast_t* field,
  ast_t* class_members, ast_t* create_params, ast_t* create_body,
  ast_t* call_args)
{
  AST_GET_CHILDREN(field, id, type, init);
  ast_t* p_id = ast_from_string(id, package_hygienic_id(&opt->check));

  // The param is: $0: type
  BUILD(param, field,
    NODE(TK_PARAM,
      TREE(p_id)
      TREE(type)
      NONE));

  // The arg is: $seq init
  BUILD(arg, init,
    NODE(TK_SEQ,
      TREE(init)));

  // The body of create contains: id = consume $0
  BUILD(assign, init,
    NODE(TK_ASSIGN,
      NODE(TK_CONSUME, NODE(TK_NONE) NODE(TK_REFERENCE, TREE(p_id)))
      NODE(TK_REFERENCE, TREE(id))));

  // Remove the initialiser from the field
  ast_replace(&init, ast_from(init, TK_NONE));

  ast_add(class_members, field);
  ast_append(create_params, param);
  ast_append(create_body, assign);
  ast_append(call_args, arg);
}
Esempio n. 10
0
static bool push_assume(ast_t* sub, ast_t* super)
{
  // Returns true if we have already assumed sub is a subtype of super.
  if(subtype_assume != NULL)
  {
    ast_t* assumption = ast_child(subtype_assume);

    while(assumption != NULL)
    {
      AST_GET_CHILDREN(assumption, assume_sub, assume_super);

      if(exact_nominal(sub, assume_sub) &&
        exact_nominal(super, assume_super))
        return true;

      assumption = ast_sibling(assumption);
    }
  } else {
    subtype_assume = ast_from(sub, TK_NONE);
  }

  BUILD(assume, sub, NODE(TK_NONE, TREE(ast_dup(sub)) TREE(ast_dup(super))));
  ast_add(subtype_assume, assume);
  return false;
}
Esempio n. 11
0
static ast_result_t sugar_binop(ast_t** astp, const char* fn_name)
{
  AST_GET_CHILDREN(*astp, left, right);

  ast_t* positional = ast_from(right, TK_POSITIONALARGS);

  if(ast_id(right) == TK_TUPLE)
  {
    ast_t* value = ast_child(right);

    while(value != NULL)
    {
      BUILD(arg, right, NODE(TK_SEQ, TREE(value)));
      ast_append(positional, arg);
      value = ast_sibling(value);
    }
  } else {
    BUILD(arg, right, NODE(TK_SEQ, TREE(right)));
    ast_add(positional, arg);
  }

  REPLACE(astp,
    NODE(TK_CALL,
      TREE(positional)
      NONE
      NODE(TK_DOT, TREE(left) ID(fn_name))
      ));

  return AST_OK;
}
Esempio n. 12
0
builder_t* builder_create(const char* description)
{
  if(description == NULL)
    return NULL;

  source_t* source = source_open_string(description);
  symtab_t* symtab = symtab_new();

  ast_t* ast = build_ast(source, symtab);

  if(ast == NULL)
  {
    // Error, tidy up
    source_close(source);
    symtab_free(symtab);
    return NULL;
  }

  // Success, create builder
  builder_t* builder = POOL_ALLOC(builder_t);

  builder->sources = NULL;
  builder->defs = symtab;
  builder->dummy_root = ast_blank(TK_TEST);

  ast_add(builder->dummy_root, ast);
  add_source(builder, source);

  return builder;
}
Esempio n. 13
0
bool parse(ast_t* package, source_t* source, rule_t start,
  const char* expected)
{
  assert(package != NULL);
  assert(source != NULL);
  assert(expected != NULL);

  // Open the lexer
  lexer_t* lexer = lexer_open(source);

  if(lexer == NULL)
    return false;

  // Create a parser and attach the lexer
  parser_t* parser = POOL_ALLOC(parser_t);
  parser->source = source;
  parser->lexer = lexer;
  parser->token = lexer_next(lexer);
  parser->last_matched = NULL;
  parser->last_token_line = 0;
  parser->next_flags = 0;
  parser->failed = false;

  // Parse given start rule
  builder_fn_t build_fn;
  ast_t* ast = start(parser, &build_fn, expected);

  if(ast == PARSE_ERROR)
    ast = NULL;

  if(ast == RULE_NOT_FOUND)
  {
    syntax_error(parser, expected, NULL, NULL);
    ast = NULL;
  }

  if(parser->failed)
  {
    ast_free(ast);
    ast = NULL;
  }

  lexer_close(lexer);
  token_free(parser->token);
  POOL_FREE(parser_t, parser);

  if(ast == NULL)
  {
    source_close(source);
    return false;
  }

  assert(ast_id(ast) == TK_MODULE);
  assert(ast_data(ast) == NULL);
  ast_setdata(ast, source);
  ast_add(package, ast);
  return true;
}
Esempio n. 14
0
// Add the given method to the relevant name list in the given symbol table
static bool add_method_to_list(ast_t* method, methods_t* method_info,
  const char *entity_name)
{
  assert(method != NULL);
  assert(method_info != NULL);
  assert(entity_name != NULL);

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

  symtab_t* symtab = method_info->symtab;
  assert(symtab != NULL);

  // Entity doesn't yet have method, add it to our list for later
  ast_t* list = (ast_t*)symtab_find(symtab, name, NULL);

  if(list == NULL)
  {
    ast_t* case_clash = (ast_t*)symtab_find_case(symtab, name, NULL);

    if(case_clash != NULL)
    {
      ast_error(case_clash, "in %s method name differs only in case",
        entity_name);
      ast_error(method, "previous definition is here");
      return false;
    }

    // First instance of this name
    list = ast_blank(TK_ID);
    ast_set_name(list, name);
    symtab_add(symtab, name, (void*)list, SYM_NONE);

    if(method_info->last_list == NULL)
      ast_add(method_info->name_lists, list);
    else
      ast_add_sibling(method_info->last_list, list);

    method_info->last_list = list;
  }

  ast_add(list, method);
  return true;
}
Esempio n. 15
0
void infix_builder(rule_state_t* state, ast_t* new_ast)
{
  assert(state != NULL);
  assert(new_ast != NULL);

  // New AST goes at the top
  ast_add(new_ast, state->ast);
  state->ast = new_ast;
  state->last_child = NULL;
}
Esempio n. 16
0
// If the given tree is a TK_NONE expand it to a source None
static void expand_none(ast_t* ast, bool is_scope)
{
  if(ast_id(ast) != TK_NONE)
    return;

  if(is_scope)
    ast_scope(ast);

  ast_setid(ast, TK_SEQ);
  BUILD_NO_DEBUG(ref, ast, NODE(TK_REFERENCE, ID("None")));
  ast_add(ast, ref);
}
Esempio n. 17
0
File: call.c Progetto: mgist/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);

  if(!expr_seq(opt, arg))
    return false;

  return true;
}
Esempio n. 18
0
ast_t* ast_list_append(ast_t* parent, ast_t** last_pointer, ast_t* new_child)
{
  assert(parent != NULL);
  assert(last_pointer != NULL);
  assert(new_child != NULL);

  if(*last_pointer == NULL)
  {
    // This is the first child for the parent
    assert(parent->child == NULL);
    *last_pointer = ast_add(parent, new_child);
  }
  else
  {
    // This is not the first child, append to list
    assert(parent->child != NULL);
    assert((*last_pointer)->sibling == NULL);
    *last_pointer = ast_add_sibling(*last_pointer, new_child);
  }

  return *last_pointer;
}
Esempio n. 19
0
static ast_result_t sugar_module(ast_t* ast)
{
  ast_t* docstring = ast_child(ast);

  ast_t* package = ast_parent(ast);
  assert(ast_id(package) == TK_PACKAGE);

  if(strcmp(package_name(package), "$0") != 0)
  {
    // Every module not in builtin has an implicit use builtin command.
    // Since builtin is always the first package processed it is $0.
    BUILD(builtin, ast,
      NODE(TK_USE,
      NONE
      STRING(stringtab("builtin"))
      NONE));

    ast_add(ast, builtin);
  }

  if((docstring == NULL) || (ast_id(docstring) != TK_STRING))
    return AST_OK;

  ast_t* package_docstring = ast_childlast(package);

  if(ast_id(package_docstring) == TK_STRING)
  {
    ast_error(docstring, "the package already has a docstring");
    ast_error(package_docstring, "the existing docstring is here");
    return AST_ERROR;
  }

  ast_append(package, docstring);
  ast_remove(docstring);
  return AST_OK;
}
Esempio n. 20
0
static void reify_reference(ast_t** astp, ast_t* typeparam, ast_t* typearg)
{
  ast_t* ast = *astp;
  pony_assert(ast_id(ast) == TK_REFERENCE);

  const char* name = ast_name(ast_child(ast));

  sym_status_t status;
  ast_t* ref_def = ast_get(ast, name, &status);

  if(ref_def == NULL)
    return;

  ast_t* param_def = (ast_t*)ast_data(typeparam);
  pony_assert(param_def != NULL);

  if(ref_def != param_def)
    return;

  ast_setid(ast, TK_TYPEREF);
  ast_add(ast, ast_from(ast, TK_NONE));    // 1st child: package reference
  ast_append(ast, ast_from(ast, TK_NONE)); // 3rd child: type args
  ast_settype(ast, typearg);
}
Esempio n. 21
0
bool expr_qualify(pass_opt_t* opt, ast_t** astp)
{
  // Left is a postfix expression, right is a typeargs.
  ast_t* ast = *astp;
  AST_GET_CHILDREN(ast, left, right);
  ast_t* type = ast_type(left);
  assert(ast_id(right) == TK_TYPEARGS);

  if(is_typecheck_error(type))
    return false;

  switch(ast_id(left))
  {
    case TK_TYPEREF:
    {
      // Qualify the type.
      assert(ast_id(type) == TK_NOMINAL);

      // If the type isn't polymorphic or the type is already qualified,
      // sugar .apply().
      ast_t* def = names_def(opt, type);
      ast_t* typeparams = ast_childidx(def, 1);

      if((ast_id(typeparams) == TK_NONE) ||
        (ast_id(ast_childidx(type, 2)) != TK_NONE))
      {
        if(!expr_nominal(opt, &type))
          return false;

        break;
      }

      type = ast_dup(type);
      ast_t* typeargs = ast_childidx(type, 2);
      ast_replace(&typeargs, right);
      ast_settype(ast, type);
      ast_setid(ast, TK_TYPEREF);

      return expr_typeref(opt, astp);
    }

    case TK_NEWREF:
    case TK_NEWBEREF:
    case TK_BEREF:
    case TK_FUNREF:
    case TK_NEWAPP:
    case TK_BEAPP:
    case TK_FUNAPP:
    {
      // Qualify the function.
      assert(ast_id(type) == TK_FUNTYPE);
      ast_t* typeparams = ast_childidx(type, 1);

      if(!reify_defaults(typeparams, right, true, opt))
        return false;

      if(!check_constraints(left, typeparams, right, true, opt))
        return false;

      type = reify(type, typeparams, right, opt);
      typeparams = ast_childidx(type, 1);
      ast_replace(&typeparams, ast_from(typeparams, TK_NONE));

      ast_settype(ast, type);
      ast_setid(ast, ast_id(left));
      ast_inheritflags(ast);
      return true;
    }

    default: {}
  }

  // Sugar .apply()
  ast_t* dot = ast_from(left, TK_DOT);
  ast_add(dot, ast_from_string(left, "apply"));
  ast_swap(left, dot);
  ast_add(dot, left);

  if(!expr_dot(opt, &dot))
    return false;

  return expr_qualify(opt, astp);
}
Esempio n. 22
0
static bool type_access(pass_opt_t* opt, ast_t** astp)
{
  ast_t* ast = *astp;

  // Left is a typeref, right is an id.
  ast_t* left = ast_child(ast);
  ast_t* right = ast_sibling(left);
  ast_t* type = ast_type(left);

  if(is_typecheck_error(type))
    return false;

  assert(ast_id(left) == TK_TYPEREF);
  assert(ast_id(right) == TK_ID);

  ast_t* find = lookup(opt, ast, type, ast_name(right));

  if(find == NULL)
    return false;

  bool ret = true;

  switch(ast_id(find))
  {
    case TK_TYPEPARAM:
      ast_error(opt->check.errors, right,
        "can't look up a typeparam on a type");
      ret = false;
      break;

    case TK_NEW:
      ret = method_access(opt, ast, find);
      break;

    case TK_FVAR:
    case TK_FLET:
    case TK_EMBED:
    case TK_BE:
    case TK_FUN:
    {
      // Make this a lookup on a default constructed object.
      ast_free_unattached(find);

      if(!strcmp(ast_name(right), "create"))
      {
        ast_error(opt->check.errors, right,
          "create is not a constructor on this type");
        return false;
      }

      ast_t* dot = ast_from(ast, TK_DOT);
      ast_add(dot, ast_from_string(ast, "create"));
      ast_swap(left, dot);
      ast_add(dot, left);

      ast_t* call = ast_from(ast, TK_CALL);
      ast_swap(dot, call);
      ast_add(call, dot); // the LHS goes at the end, not the beginning
      ast_add(call, ast_from(ast, TK_NONE)); // named
      ast_add(call, ast_from(ast, TK_NONE)); // positional

      if(!expr_dot(opt, &dot))
        return false;

      if(!expr_call(opt, &call))
        return false;

      return expr_dot(opt, astp);
    }

    default:
      assert(0);
      ret = false;
      break;
  }

  ast_free_unattached(find);
  return ret;
}
Esempio n. 23
0
bool expr_reference(pass_opt_t* opt, ast_t** astp)
{
  typecheck_t* t = &opt->check;
  ast_t* ast = *astp;

  // Everything we reference must be in scope.
  const char* name = ast_name(ast_child(ast));

  sym_status_t status;
  ast_t* def = ast_get(ast, name, &status);

  if(def == NULL)
  {
    const char* alt_name = suggest_alt_name(ast, name);

    if(alt_name == NULL)
      ast_error(ast, "can't find declaration of '%s'", name);
    else
      ast_error(ast, "can't find declaration of '%s', did you mean '%s'?",
        name, alt_name);

    return false;
  }

  switch(ast_id(def))
  {
    case TK_PACKAGE:
    {
      // Only allowed if in a TK_DOT with a type.
      if(ast_id(ast_parent(ast)) != TK_DOT)
      {
        ast_error(ast, "a package can only appear as a prefix to a type");
        return false;
      }

      ast_setid(ast, TK_PACKAGEREF);
      return true;
    }

    case TK_INTERFACE:
    case TK_TRAIT:
    case TK_TYPE:
    case TK_TYPEPARAM:
    case TK_PRIMITIVE:
    case TK_STRUCT:
    case TK_CLASS:
    case TK_ACTOR:
    {
      // It's a type name. This may not be a valid type, since it may need
      // type arguments.
      ast_t* id = ast_child(def);
      const char* name = ast_name(id);
      ast_t* type = type_sugar(ast, NULL, name);
      ast_settype(ast, type);
      ast_setid(ast, TK_TYPEREF);

      return expr_typeref(opt, astp);
    }

    case TK_FVAR:
    case TK_FLET:
    case TK_EMBED:
    {
      // Transform to "this.f".
      if(!def_before_use(def, ast, name))
        return false;

      ast_t* dot = ast_from(ast, TK_DOT);
      ast_add(dot, ast_child(ast));

      ast_t* self = ast_from(ast, TK_THIS);
      ast_add(dot, self);

      ast_replace(astp, dot);

      if(!expr_this(opt, self))
        return false;

      return expr_dot(opt, astp);
    }

    case TK_PARAM:
    {
      if(t->frame->def_arg != NULL)
      {
        ast_error(ast, "can't reference a parameter in a default argument");
        return false;
      }

      if(!def_before_use(def, ast, name))
        return false;

      ast_t* type = ast_type(def);

      if(is_typecheck_error(type))
        return false;

      if(!valid_reference(opt, ast, type, status))
        return false;

      if(!sendable(type) && (t->frame->recover != NULL))
      {
        ast_error(ast,
          "can't access a non-sendable parameter from inside a recover "
          "expression");
        return false;
      }

      // Get the type of the parameter and attach it to our reference.
      // Automatically consume a parameter if the function is done.
      ast_t* r_type = type;

      if(is_method_return(t, ast))
        r_type = consume_type(type, TK_NONE);

      ast_settype(ast, r_type);
      ast_setid(ast, TK_PARAMREF);
      return true;
    }

    case TK_NEW:
    case TK_BE:
    case TK_FUN:
    {
      // Transform to "this.f".
      ast_t* dot = ast_from(ast, TK_DOT);
      ast_add(dot, ast_child(ast));

      ast_t* self = ast_from(ast, TK_THIS);
      ast_add(dot, self);

      ast_replace(astp, dot);

      if(!expr_this(opt, self))
        return false;

      return expr_dot(opt, astp);
    }

    case TK_ID:
    {
      if(!def_before_use(def, ast, name))
        return false;

      ast_t* type = ast_type(def);

      if(type != NULL && ast_id(type) == TK_INFERTYPE)
      {
        ast_error(ast, "cannot infer type of %s\n", ast_nice_name(def));
        ast_settype(def, ast_from(def, TK_ERRORTYPE));
        ast_settype(ast, ast_from(ast, TK_ERRORTYPE));
        return false;
      }

      if(is_typecheck_error(type))
        return false;

      if(!valid_reference(opt, ast, type, status))
        return false;

      ast_t* var = ast_parent(def);

      switch(ast_id(var))
      {
        case TK_VAR:
          ast_setid(ast, TK_VARREF);
          break;

        case TK_LET:
        case TK_MATCH_CAPTURE:
          ast_setid(ast, TK_LETREF);
          break;

        default:
          assert(0);
          return false;
      }

      if(!sendable(type))
      {
        if(t->frame->recover != NULL)
        {
          ast_t* def_recover = ast_nearest(def, TK_RECOVER);

          if(t->frame->recover != def_recover)
          {
            ast_error(ast, "can't access a non-sendable local defined outside "
              "of a recover expression from within that recover expression");
            return false;
          }
        }
      }

      // Get the type of the local and attach it to our reference.
      // Automatically consume a local if the function is done.
      ast_t* r_type = type;

      if(is_method_return(t, ast))
        r_type = consume_type(type, TK_NONE);

      ast_settype(ast, r_type);
      return true;
    }

    default: {}
  }

  assert(0);
  return false;
}
Esempio n. 24
0
bool expr_typeref(pass_opt_t* opt, ast_t** astp)
{
  ast_t* ast = *astp;
  assert(ast_id(ast) == TK_TYPEREF);
  ast_t* type = ast_type(ast);

  if(is_typecheck_error(type))
    return false;

  switch(ast_id(ast_parent(ast)))
  {
    case TK_QUALIFY:
      // Doesn't have to be valid yet.
      break;

    case TK_DOT:
      // Has to be valid.
      if(!expr_nominal(opt, &type))
      {
        ast_settype(ast, ast_from(type, TK_ERRORTYPE));
        ast_free_unattached(type);
        return false;
      }
      break;

    case TK_CALL:
    {
      // Has to be valid.
      if(!expr_nominal(opt, &type))
      {
        ast_settype(ast, ast_from(type, TK_ERRORTYPE));
        ast_free_unattached(type);
        return false;
      }

      // Transform to a default constructor.
      ast_t* dot = ast_from(ast, TK_DOT);
      ast_add(dot, ast_from_string(ast, "create"));
      ast_swap(ast, dot);
      *astp = dot;
      ast_add(dot, ast);

      if(!expr_dot(opt, astp))
      {
        ast_settype(ast, ast_from(type, TK_ERRORTYPE));
        ast_free_unattached(type);
        return false;
      }

      ast_t* ast = *astp;

      // If the default constructor has no parameters, transform to an apply
      // call.
      if((ast_id(ast) == TK_NEWREF) || (ast_id(ast) == TK_NEWBEREF))
      {
        type = ast_type(ast);

        if(is_typecheck_error(type))
          return false;

        assert(ast_id(type) == TK_FUNTYPE);
        AST_GET_CHILDREN(type, cap, typeparams, params, result);

        if(ast_id(params) == TK_NONE)
        {
          // Add a call node.
          ast_t* call = ast_from(ast, TK_CALL);
          ast_add(call, ast_from(call, TK_NONE)); // Named
          ast_add(call, ast_from(call, TK_NONE)); // Positional
          ast_swap(ast, call);
          ast_append(call, ast);

          if(!expr_call(opt, &call))
          {
            ast_settype(ast, ast_from(type, TK_ERRORTYPE));
            ast_free_unattached(type);
            return false;
          }

          // Add a dot node.
          ast_t* apply = ast_from(call, TK_DOT);
          ast_add(apply, ast_from_string(call, "apply"));
          ast_swap(call, apply);
          ast_add(apply, call);

          if(!expr_dot(opt, &apply))
          {
            ast_settype(ast, ast_from(type, TK_ERRORTYPE));
            ast_free_unattached(type);
            return false;
          }
        }
      }

      return true;
    }

    default:
    {
      // Has to be valid.
      if(!expr_nominal(opt, &type))
      {
        ast_settype(ast, ast_from(type, TK_ERRORTYPE));
        ast_free_unattached(type);
        return false;
      }

      // Transform to a default constructor.
      ast_t* dot = ast_from(ast, TK_DOT);
      ast_add(dot, ast_from_string(ast, "create"));
      ast_swap(ast, dot);
      ast_add(dot, ast);

      // Call the default constructor with no arguments.
      ast_t* call = ast_from(ast, TK_CALL);
      ast_swap(dot, call);
      ast_add(call, dot); // Receiver comes last.
      ast_add(call, ast_from(ast, TK_NONE)); // Named args.
      ast_add(call, ast_from(ast, TK_NONE)); // Positional args.

      *astp = call;

      if(!expr_dot(opt, &dot))
      {
        ast_settype(ast, ast_from(type, TK_ERRORTYPE));
        ast_free_unattached(type);
        return false;
      }

      if(!expr_call(opt, astp))
      {
        ast_settype(ast, ast_from(type, TK_ERRORTYPE));
        ast_free_unattached(type);
        return false;
      }
      break;
    }
  }

  return true;
}
Esempio n. 25
0
static ast_result_t sugar_entity(typecheck_t* t, ast_t* ast, bool add_create,
  token_id def_def_cap)
{
  AST_GET_CHILDREN(ast, id, typeparams, defcap, traits, members);

  if(add_create)
    add_default_constructor(ast);

  if(ast_id(defcap) == TK_NONE)
    ast_setid(defcap, def_def_cap);

  // Build a reverse sequence of all field initialisers.
  BUILD(init_seq, members, NODE(TK_SEQ));
  ast_t* member = ast_child(members);

  while(member != NULL)
  {
    switch(ast_id(member))
    {
      case TK_FLET:
      case TK_FVAR:
      case TK_EMBED:
      {
        AST_GET_CHILDREN(member, f_id, f_type, f_init);

        if(ast_id(f_init) != TK_NONE)
        {
          // Replace the initialiser with TK_NONE.
          ast_swap(f_init, ast_from(f_init, TK_NONE));

          // id = init
          BUILD(init, member,
            NODE(TK_ASSIGN,
            TREE(f_init)
            NODE(TK_REFERENCE, TREE(f_id))));

          ast_add(init_seq, init);
        }
        break;
      }

      default: {}
    }

    member = ast_sibling(member);
  }

  // Add field initialisers to all constructors.
  if(ast_child(init_seq) != NULL)
  {
    member = ast_child(members);

    while(member != NULL)
    {
      switch(ast_id(member))
      {
        case TK_NEW:
        {
          AST_GET_CHILDREN(member, n_cap, n_id, n_typeparam, n_params,
            n_result, n_partial, n_body);

          assert(ast_id(n_body) == TK_SEQ);

          ast_t* init = ast_child(init_seq);

          while(init != NULL)
          {
            ast_add(n_body, init);
            init = ast_sibling(init);
          }
          break;
        }

        default: {}
      }

      member = ast_sibling(member);
    }
  }

  ast_free_unattached(init_seq);

  return sugar_case_methods(t, ast);
}
Esempio n. 26
0
static bool partial_application(pass_opt_t* opt, ast_t** astp)
{
  ast_t* ast = *astp;
  typecheck_t* t = &opt->check;

  if(!method_application(opt, ast, true))
    return false;

  AST_GET_CHILDREN(ast, positional, namedargs, lhs);

  // LHS must be a TK_TILDE, possibly contained in a TK_QUALIFY.
  AST_GET_CHILDREN(lhs, receiver, method);

  switch(ast_id(receiver))
  {
    case TK_NEWAPP:
    case TK_BEAPP:
    case TK_FUNAPP:
      AST_GET_CHILDREN_NO_DECL(receiver, receiver, method);
      break;

    default: {}
  }

  // The TK_FUNTYPE of the LHS.
  ast_t* type = ast_type(lhs);

  if(is_typecheck_error(type))
    return false;

  token_id apply_cap = partial_application_cap(type, receiver, positional);
  AST_GET_CHILDREN(type, cap, typeparams, params, result);

  // Create a new anonymous type.
  ast_t* c_id = ast_from_string(ast, package_hygienic_id(t));

  BUILD(def, ast,
    NODE(TK_CLASS, AST_SCOPE
      TREE(c_id)
      NONE
      NONE
      NONE
      NODE(TK_MEMBERS)
      NONE
      NONE));

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

  // We will have an apply method in the type.
  token_id can_error = ast_canerror(lhs) ? TK_QUESTION : TK_NONE;

  BUILD(apply, ast,
    NODE(TK_FUN, AST_SCOPE
      NODE(apply_cap)
      ID("apply")
      NONE
      NODE(TK_PARAMS)
      TREE(result)
      NODE(can_error)
      NODE(TK_SEQ)
      NONE));

  // We will replace partial application with $0.create(...)
  BUILD(call_receiver, ast, NODE(TK_REFERENCE, TREE(c_id)));
  BUILD(call_dot, ast, NODE(TK_DOT, TREE(call_receiver) ID("create")));

  BUILD(call, ast,
    NODE(TK_CALL,
      NONE
      NODE(TK_NAMEDARGS)
      TREE(call_dot)));

  ast_t* class_members = ast_childidx(def, 4);
  ast_t* create_params = ast_childidx(create, 3);
  ast_t* create_body = ast_childidx(create, 6);
  ast_t* apply_params = ast_childidx(apply, 3);
  ast_t* apply_body = ast_childidx(apply, 6);
  ast_t* call_namedargs = ast_childidx(call, 1);

  // Add the receiver to the anonymous type.
  ast_t* r_id = ast_from_string(receiver, package_hygienic_id(t));
  ast_t* r_field_id = ast_from_string(receiver, package_hygienic_id(t));
  ast_t* r_type = ast_type(receiver);

  if(is_typecheck_error(r_type))
    return false;

  // A field in the type.
  BUILD(r_field, receiver, NODE(TK_FLET, TREE(r_field_id) TREE(r_type) NONE));

  // A parameter of the constructor.
  BUILD(r_ctor_param, receiver, NODE(TK_PARAM, TREE(r_id) TREE(r_type) NONE));

  // An assignment in the constructor body.
  BUILD(r_assign, receiver,
    NODE(TK_ASSIGN,
      NODE(TK_CONSUME, NODE(TK_NONE) NODE(TK_REFERENCE, TREE(r_id)))
      NODE(TK_REFERENCE, TREE(r_field_id))));

  // A named argument at the call site.
  BUILD(r_call_seq, receiver, NODE(TK_SEQ, TREE(receiver)));
  BUILD(r_call_arg, receiver, NODE(TK_NAMEDARG, TREE(r_id) TREE(r_call_seq)));
  ast_settype(r_call_seq, r_type);

  ast_append(class_members, r_field);
  ast_append(create_params, r_ctor_param);
  ast_append(create_body, r_assign);
  ast_append(call_namedargs, r_call_arg);

  // Add a call to the original method to the apply body.
  BUILD(apply_call, ast,
    NODE(TK_CALL,
      NODE(TK_POSITIONALARGS)
      NONE
      NODE(TK_DOT, NODE(TK_REFERENCE, TREE(r_field_id)) TREE(method))));

  ast_append(apply_body, apply_call);
  ast_t* apply_args = ast_child(apply_call);

  // Add the arguments to the anonymous type.
  ast_t* arg = ast_child(positional);
  ast_t* param = ast_child(params);

  while(arg != NULL)
  {
    AST_GET_CHILDREN(param, id, p_type);

    if(ast_id(arg) == TK_NONE)
    {
      // A parameter of the apply method, using the same name, type and default
      // argument.
      ast_append(apply_params, param);

      // An arg in the call to the original method.
      BUILD(apply_arg, param,
        NODE(TK_SEQ,
          NODE(TK_CONSUME,
            NODE(TK_NONE)
            NODE(TK_REFERENCE, TREE(id)))));

      ast_append(apply_args, apply_arg);
    } else {
      ast_t* p_id = ast_from_string(id, package_hygienic_id(t));

      // A field in the type.
      BUILD(field, arg, NODE(TK_FLET, TREE(id) TREE(p_type) NONE));

      // A parameter of the constructor.
      BUILD(ctor_param, arg, NODE(TK_PARAM, TREE(p_id) TREE(p_type) NONE));

      // An assignment in the constructor body.
      BUILD(assign, arg,
        NODE(TK_ASSIGN,
          NODE(TK_CONSUME, NODE(TK_NONE) NODE(TK_REFERENCE, TREE(p_id)))
          NODE(TK_REFERENCE, TREE(id))));

      // A named argument at the call site.
      BUILD(call_arg, arg, NODE(TK_NAMEDARG, TREE(p_id) TREE(arg)));

      // An arg in the call to the original method.
      BUILD(apply_arg, arg, NODE(TK_SEQ, NODE(TK_REFERENCE, TREE(id))));

      ast_append(class_members, field);
      ast_append(create_params, ctor_param);
      ast_append(create_body, assign);
      ast_append(call_namedargs, call_arg);
      ast_append(apply_args, apply_arg);
    }

    arg = ast_sibling(arg);
    param = ast_sibling(param);
  }

  // Add create and apply to the anonymous type.
  ast_append(class_members, create);
  ast_append(class_members, apply);

  // Typecheck the anonymous type.
  ast_add(t->frame->module, def);

  if(!type_passes(def, opt))
    return false;

  // Typecheck the create call.
  if(!expr_reference(opt, &call_receiver))
    return false;

  if(!expr_dot(opt, &call_dot))
    return false;

  if(!expr_call(opt, &call))
    return false;

  // Replace the partial application with the create call.
  ast_replace(astp, call);
  return true;
}
Esempio n. 27
0
// Load a sequence of nodes until the specified terminator is found
static ast_t* get_nodes(build_parser_t* builder, ast_token_id terminator)
{
  assert(builder != NULL);

  ast_t* ast = NULL;
  ast_t* last_child = NULL;

  while(true)
  {
    ast_token_id id = get_token(builder);
    ast_t* child = NULL;
    bool is_type = false;

    if(id == terminator)
    {
      if(ast == NULL)
        build_error(builder, "Syntax error");

      if(ast_id(ast) == TK_MINUS && ast_childcount(ast) == 1)
        ast_setid(ast, TK_UNARY_MINUS);

      return ast;
    }

    if(id == AT_ID)
      id = keyword_replace(builder);

    switch(id)
    {
      case AT_LPAREN:
        child = get_nodes(builder, AT_RPAREN);
        break;

      case AT_LSQUARE:
        child = get_type(builder, ast);
        is_type = true;
        break;

      case AT_ERROR:  // Propogate
        break;

      case AT_STRING:
      case AT_TOKEN:
        child = ast_token(builder->token);
        save_token(builder);
        get_attributes(builder, child);
        break;

      case AT_ID:
        if(strcmp("id", token_string(builder->token)) == 0)
          return get_id(builder, ast);

        build_error(builder, "Unrecognised identifier \"%s\"",
          token_string(builder->token));
        break;

      default:
        build_error(builder, "Syntax error");
        break;
    }

    if(child == NULL)
    {
      // An error occurred and should already have been reported
      ast_free(ast);
      return NULL;
    }

    if(ast == NULL)
    {
      ast = child;
      last_child = NULL;
    }
    else if(is_type)
    {
      ast_settype(ast, child);
    }
    else
    {
      if(last_child == NULL)
        ast_add(ast, child);
      else
        ast_add_sibling(last_child, child);

      last_child = child;
    }
  }
}