Пример #1
0
ast_result_t pass_expr(ast_t** astp, pass_opt_t* options)
{
  typecheck_t* t = &options->check;
  ast_t* ast = *astp;
  bool r = true;

  switch(ast_id(ast))
  {
    case TK_NOMINAL:    r = expr_nominal(options, astp); break;
    case TK_FVAR:
    case TK_FLET:
    case TK_PARAM:      r = expr_field(options, ast); break;
    case TK_NEW:
    case TK_BE:
    case TK_FUN:        r = expr_fun(options, ast); break;
    case TK_SEQ:        r = expr_seq(ast); break;
    case TK_VAR:
    case TK_LET:        r = expr_local(t, ast); break;
    case TK_BREAK:      r = expr_break(t, ast); break;
    case TK_CONTINUE:   r = expr_continue(t, ast); break;
    case TK_RETURN:     r = expr_return(options, ast); break;
    case TK_IS:
    case TK_ISNT:       r = expr_identity(options, ast); break;
    case TK_ASSIGN:     r = expr_assign(options, ast); break;
    case TK_CONSUME:    r = expr_consume(t, ast); break;
    case TK_RECOVER:    r = expr_recover(ast); break;
    case TK_DOT:        r = expr_dot(options, astp); break;
    case TK_TILDE:      r = expr_tilde(options, astp); break;
    case TK_QUALIFY:    r = expr_qualify(options, astp); break;
    case TK_CALL:       r = expr_call(options, astp); break;
    case TK_IF:         r = expr_if(options, ast); break;
    case TK_WHILE:      r = expr_while(options, ast); break;
    case TK_REPEAT:     r = expr_repeat(options, ast); break;
    case TK_TRY_NO_CHECK:
    case TK_TRY:        r = expr_try(options, ast); break;
    case TK_MATCH:      r = expr_match(options, ast); break;
    case TK_CASES:      r = expr_cases(ast); break;
    case TK_CASE:       r = expr_case(options, ast); break;
    case TK_TUPLE:      r = expr_tuple(ast); break;
    case TK_ARRAY:      r = expr_array(options, astp); break;
    case TK_REFERENCE:  r = expr_reference(options, astp); break;
    case TK_THIS:       r = expr_this(options, ast); break;
    case TK_TRUE:
    case TK_FALSE:      r = expr_literal(options, ast, "Bool"); break;
    case TK_ERROR:      r = expr_error(ast); break;
    case TK_COMPILER_INTRINSIC:
                        r = expr_compiler_intrinsic(t, ast); break;
    case TK_POSITIONALARGS:
    case TK_NAMEDARGS:
    case TK_NAMEDARG:
    case TK_UPDATEARG:  ast_inheritflags(ast); break;
    case TK_AMP:        r = expr_addressof(options, ast); break;
    case TK_DONTCARE:   r = expr_dontcare(ast); break;

    case TK_INT:
      // Integer literals can be integers or floats
      make_literal_type(ast);
      break;

    case TK_FLOAT:
      make_literal_type(ast);
      break;

    case TK_STRING:
      if(ast_id(ast_parent(ast)) == TK_PACKAGE)
        return AST_OK;

      r = expr_literal(options, ast, "String");
      break;

    case TK_FFICALL:
      return expr_ffi(options, ast);

    default: {}
  }

  if(!r)
  {
    assert(get_error_count() > 0);
    return AST_ERROR;
  }

  // Can't use ast here, it might have changed
  symtab_t* symtab = ast_get_symtab(*astp);

  if(symtab != NULL && !symtab_check_all_defined(symtab))
    return AST_ERROR;

  return AST_OK;
}
Пример #2
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;
}