Beispiel #1
0
static bool apply_named_args(pass_opt_t* opt, ast_t* params, ast_t* positional,
  ast_t* namedargs)
{
  ast_t* namedarg = ast_pop(namedargs);

  while(namedarg != NULL)
  {
    AST_GET_CHILDREN(namedarg, arg_id, arg);

    ast_t* param = ast_child(params);
    size_t param_index = 0;

    while(param != NULL)
    {
      AST_GET_CHILDREN(param, param_id);

      if(ast_name(arg_id) == ast_name(param_id))
        break;

      param = ast_sibling(param);
      param_index++;
    }

    if(param == NULL)
    {
      if(ast_id(namedarg) == TK_UPDATEARG)
      {
        ast_error(opt->check.errors, arg_id,
          "cannot use sugar, update() has no parameter named \"value\"");
        return false;
      }

      ast_error(opt->check.errors, arg_id, "not a parameter name");
      return false;
    }

    ast_t* arg_replace = ast_childidx(positional, param_index);

    if(ast_id(arg_replace) != TK_NONE)
    {
      ast_error(opt->check.errors, arg_id,
        "named argument is already supplied");
      ast_error_continue(opt->check.errors, arg_replace,
        "supplied argument is here");
      return false;
    }

    // Extract named argument expression to avoid copying it
    ast_free(ast_pop(namedarg));  // ID
    arg = ast_pop(namedarg);  // Expression

    ast_replace(&arg_replace, arg);
    namedarg = ast_pop(namedargs);
  }

  ast_setid(namedargs, TK_NONE);
  return true;
}
Beispiel #2
0
ast_t* program_load(const char* path, pass_opt_t* options)
{
  ast_t* program = ast_blank(TK_PROGRAM);
  ast_scope(program);

  options->program_pass = PASS_PARSE;

  // Always load builtin package first, then the specified one.
  if(package_load(program, stringtab("builtin"), options) == NULL ||
    package_load(program, path, options) == NULL)
  {
    ast_free(program);
    return NULL;
  }

  // Reorder packages so specified package is first.
  ast_t* builtin = ast_pop(program);
  ast_append(program, builtin);

  if(!ast_passes_program(program, options))
  {
    ast_free(program);
    return NULL;
  }

  return program;
}
Beispiel #3
0
// Handle the given case method parameter, which does not have a type
// specified.
// Check the case parameter and generate the pattern element.
// Returns: true on success, false on error.
static bool param_without_type(ast_t* case_param, ast_t* pattern)
{
  assert(case_param != NULL);
  assert(pattern != NULL);

  AST_GET_CHILDREN(case_param, value, type, def_arg);
  assert(ast_id(type) == TK_NONE);

  if(ast_id(def_arg) != TK_NONE)
  {
    ast_error(type,
      "cannot specify default argument for match value parameter");
    return false;
  }

  // Add value to match pattern. Pop it first to avoid pointless copy.
  ast_t* popped_value = ast_pop(case_param);

  if(ast_id(popped_value) == TK_DONTCARE)
  {
    // Value is just `don't care`.
    ast_append(pattern, popped_value);
  }
  else
  {
    // Value in an expression, need a containing sequence.
    BUILD(value_ast, value, NODE(TK_SEQ, TREE(popped_value)));
    ast_append(pattern, value_ast);
  }

  return true;
}
Beispiel #4
0
void default_builder(rule_state_t* state, ast_t* new_ast)
{
  assert(state != NULL);
  assert(new_ast != NULL);

  // Existing AST goes at the top

  if(ast_id(new_ast) == TK_FLATTEN)
  {
    // Add the children of the new node, not the node itself
    ast_t* new_child;

    while((new_child = ast_pop(new_ast)) != NULL)
      default_builder(state, new_child);

    ast_free(new_ast);
    return;
  }

  if(state->last_child == NULL)  // No valid last pointer
    ast_append(state->ast, new_ast);
  else  // Add new AST to end of children
    ast_add_sibling(state->last_child, new_ast);

  state->last_child = new_ast;
}
Beispiel #5
0
// Sugar any case methods in the given entity.
ast_result_t sugar_case_methods(typecheck_t* t, ast_t* entity)
{
  assert(entity != NULL);

  ast_result_t result = AST_OK;
  ast_t* members = ast_childidx(entity, 4);
  bool cases_found = false;

  // Check each method to see if its name is repeated, which indicates a case
  // method.
  for(ast_t* p = ast_child(members); p != NULL; p = ast_sibling(p))
  {
    if(ast_id(p) == TK_FUN || ast_id(p) == TK_BE)
    {
      const char* p_name = ast_name(ast_childidx(p, 1));

      // Check if any subsequent methods share p's name.
      for(ast_t* q = ast_sibling(p); q != NULL; q = ast_sibling(q))
      {
        if(ast_id(q) == TK_FUN || ast_id(q) == TK_BE)
        {
          const char* q_name = ast_name(ast_childidx(q, 1));

          if(q_name == p_name)
          {
            // p's name is repeated, it's a case method, get a match method.
            if(!sugar_case_method(p, members, p_name, t))
              result = AST_ERROR;

            cases_found = true;
            break;
          }
        }
      }
    }
  }

  if(cases_found)
  {
    // Remove nodes marked during processing.
    ast_t* filtered_members = ast_from(members, TK_MEMBERS);
    ast_t* list = NULL;
    ast_t* member;

    while((member = ast_pop(members)) != NULL)
    {
      if(ast_id(member) == TK_NONE) // Marked for removal.
        ast_free(member);
      else  // Put back in filtered list.
        ast_list_append(filtered_members, &list, member);
    }

    ast_replace(&members, filtered_members);
  }

  return result;
}
Beispiel #6
0
// Handle the given case method parameter list.
// Combine and check the case parameters against the existing match parameters
// and generate the match pattern to go in the worker method.
// Returns: match pattern, NULL on error.
static ast_t* process_params(ast_t* match_params, ast_t* case_params)
{
  assert(match_params != NULL);
  assert(case_params != NULL);

  bool ok = true;
  size_t count = 0;
  ast_t* pattern = ast_from(case_params, TK_TUPLE);
  ast_t* case_param = ast_child(case_params);
  ast_t* match_param = ast_child(match_params);

  while(case_param != NULL && match_param != NULL)
  {
    AST_GET_CHILDREN(case_param, id, type, def_arg);

    if(ast_id(type) != TK_NONE)
    {
      // We have a parameter.
      if(!param_with_type(case_param, match_param, pattern))
        ok = false;
    }
    else
    {
      // We have a value to match.
      if(!param_without_type(case_param, pattern))
        ok = false;
    }

    case_param = ast_sibling(case_param);
    match_param = ast_sibling(match_param);
    count++;
  }

  if(case_param != NULL || match_param != NULL)
  {
    ast_error(case_params, "differing number of parameters to case methods");
    ast_error(match_params, "clashing parameter count here");
    ok = false;
  }

  if(!ok)
  {
    ast_free(pattern);
    return NULL;
  }

  assert(count > 0);
  if(count == 1)
  {
    // Only one parameter, don't need a tuple pattern.
    ast_t* tmp = ast_pop(ast_child(pattern));
    ast_free(pattern);
    pattern = tmp;
  }

  return pattern;
}
Beispiel #7
0
static void pop_assume()
{
  ast_t* assumption = ast_pop(subtype_assume);
  ast_free_unattached(assumption);

  if(ast_child(subtype_assume) == NULL)
  {
    ast_free_unattached(subtype_assume);
    subtype_assume = NULL;
  }
}
Beispiel #8
0
static void add_as_type(typecheck_t* t, ast_t* type, ast_t* pattern,
  ast_t* body)
{
  assert(type != NULL);

  switch(ast_id(type))
  {
    case TK_TUPLETYPE:
    {
      BUILD(tuple_pattern, pattern, NODE(TK_SEQ, NODE(TK_TUPLE)));
      ast_append(pattern, tuple_pattern);
      ast_t* pattern_child = ast_child(tuple_pattern);

      BUILD(tuple_body, body, NODE(TK_SEQ, NODE(TK_TUPLE)));
      ast_t* body_child = ast_child(tuple_body);

      for(ast_t* p = ast_child(type); p != NULL; p = ast_sibling(p))
        add_as_type(t, p, pattern_child, body_child);

      if(ast_childcount(body_child) == 1)
      {
        // Only one child, not actually a tuple
        ast_t* t = ast_pop(body_child);
        ast_free(tuple_body);
        tuple_body = t;
      }

      ast_append(body, tuple_body);
      break;
    }

    case TK_DONTCARE:
      ast_append(pattern, type);
      break;

    default:
    {
      const char* name = package_hygienic_id(t);
      ast_t* a_type = alias(type);

      BUILD(pattern_elem, pattern,
        NODE(TK_SEQ,
          NODE(TK_LET, ID(name) TREE(a_type))));

      BUILD(body_elem, body,
        NODE(TK_SEQ,
          NODE(TK_CONSUME, NODE(TK_BORROWED) NODE(TK_REFERENCE, ID(name)))));

      ast_append(pattern, pattern_elem);
      ast_append(body, body_elem);
      break;
    }
  }
}
Beispiel #9
0
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);
}
Beispiel #10
0
void ast_reorder_children(ast_t* ast, const size_t* new_order,
  ast_t** shuffle_space)
{
  assert(ast != NULL);
  assert(new_order != NULL);
  assert(shuffle_space != NULL);

  size_t count = ast_childcount(ast);

  for(size_t i = 0; i < count; i++)
    shuffle_space[i] = ast_pop(ast);

  for(size_t i = 0; i < count; i++)
  {
    size_t index = new_order[i];
    assert(index < count);
    ast_t* t = shuffle_space[index];
    assert(t != NULL);
    ast_append(ast, t);
    shuffle_space[index] = NULL;
  }
}
Beispiel #11
0
static void sugar_docstring(ast_t* ast)
{
  assert(ast != NULL);

  AST_GET_CHILDREN(ast, cap, id, type_params, params, return_type,
    error, body, docstring);

  if(ast_id(docstring) == TK_NONE)
  {
    ast_t* first = ast_child(body);

    // First expression in body is a docstring if it is a string literal and
    // there are any other expressions in the body sequence
    if((first != NULL) &&
      (ast_id(first) == TK_STRING) &&
      (ast_sibling(first) != NULL))
    {
      ast_pop(body);
      ast_replace(&docstring, first);
    }
  }
}
Beispiel #12
0
// Normalise the given ifdef condition.
static void cond_normalise(ast_t** astp)
{
  pony_assert(astp != NULL);

  ast_t* ast = *astp;
  pony_assert(ast != NULL);

  switch(ast_id(ast))
  {
    case TK_AND:
    {
      ast_setid(ast, TK_IFDEFAND);

      AST_GET_CHILDREN(ast, left, right, question);
      pony_assert(ast_id(question) == TK_NONE);
      ast_remove(question);
      cond_normalise(&left);
      cond_normalise(&right);
      break;
    }

    case TK_OR:
    {
      ast_setid(ast, TK_IFDEFOR);

      AST_GET_CHILDREN(ast, left, right, question);
      pony_assert(ast_id(question) == TK_NONE);
      ast_remove(question);
      cond_normalise(&left);
      cond_normalise(&right);
      break;
    }

    case TK_NOT:
    {
      ast_setid(ast, TK_IFDEFNOT);

      AST_GET_CHILDREN(ast, child);
      cond_normalise(&child);
      break;
    }

    case TK_STRING:
    {
      ast_setid(ast, TK_ID);

      REPLACE(astp,
        NODE(TK_IFDEFFLAG,
          TREE(*astp)));

      break;
    }

    case TK_REFERENCE:
    {
      const char* name = ast_name(ast_child(ast));
      if(strcmp(name, OS_POSIX_NAME) == 0)
      {
        REPLACE(astp,
          NODE(TK_IFDEFOR,
            NODE(TK_IFDEFOR,
              NODE(TK_IFDEFFLAG, ID(OS_LINUX_NAME))
              NODE(TK_IFDEFFLAG, ID(OS_MACOSX_NAME)))
            NODE(TK_IFDEFFLAG, ID(OS_BSD_NAME))));
        break;
      }

      ast_setid(ast, TK_IFDEFFLAG);
      break;
    }

    case TK_SEQ:
    {
      // Remove the sequence node.
      pony_assert(ast_childcount(ast) == 1);
      ast_t* child = ast_pop(ast);
      pony_assert(child != NULL);

      cond_normalise(&child);
      ast_replace(astp, child);
      break;
    }

    case TK_IFDEFAND:
    case TK_IFDEFOR:
    case TK_IFDEFNOT:
    case TK_IFDEFFLAG:
      // Already normalised.
      break;

    default:
      pony_assert(0);
      break;
  }
}
Beispiel #13
0
// Check parameters and build the all parameter structures for a complete set
// of case methods with the given name.
// Generate match operand, worker parameters and worker call arguments.
// Returns: match operand, NULL on failure.
static ast_t* build_params(ast_t* match_params, ast_t* worker_params,
  ast_t* call_args, const char* name, typecheck_t* t)
{
  assert(match_params != NULL);
  assert(worker_params != NULL);
  assert(call_args != NULL);
  assert(name != NULL);
  assert(t != NULL);

  ast_t* match_operand = ast_from(match_params, TK_TUPLE);
  size_t count = 0;
  bool ok = true;

  for(ast_t* p = ast_child(match_params); p != NULL; p = ast_sibling(p))
  {
    AST_GET_CHILDREN(p, id, type, def_arg);
    count++;

    if(ast_id(id) == TK_NONE)
    {
      assert(ast_id(type) == TK_NONE);
      ast_error(p,
        "name and type not specified for parameter " __zu
        " of case function %s",
        count, name);
      ok = false;
    }
    else
    {
      const char* worker_param_name = package_hygienic_id(t);

      // Add parameter to match operand.
      BUILD(element, p,
        NODE(TK_SEQ,
        NODE(TK_CONSUME,
        NONE
        NODE(TK_REFERENCE, ID(worker_param_name)))));

      ast_append(match_operand, element);

      // Add parameter to worker function.
      BUILD(param, p,
        NODE(TK_PARAM,
        ID(worker_param_name)
        TREE(type)
        NONE)); // Default argument.

      ast_append(worker_params, param);

      // Add argument to worker call.
      BUILD(arg, p,
        NODE(TK_SEQ,
        NODE(TK_CONSUME,
        NONE
        NODE(TK_REFERENCE, TREE(id)))));

      ast_append(call_args, arg);
    }
  }

  if(!ok)
  {
    ast_free(match_operand);
    return NULL;
  }

  assert(count > 0);
  if(count == 1)
  {
    // Only one parameter, don't need tuple in operand.
    ast_t* tmp = ast_pop(ast_child(match_operand));
    ast_free(match_operand);
    match_operand = tmp;
  }

  return match_operand;
}