Beispiel #1
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 #2
0
void ast_clear_local(ast_t* ast)
{
  if(ast->symtab != NULL)
  {
    symtab_free(ast->symtab);
    ast_scope(ast);
  }
}
Beispiel #3
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);
}
Beispiel #4
0
/* Tidy up a successfully parsed rule.
 * Args:
 *    rule_set is a NULL terminated list.
 *    out_found reports whether an optional token was found. Only set on
 *      success. May be set to NULL if this information is not needed.
 *
 * Returns:
 *    AST created, NULL for none.
 */
ast_t* parse_rule_complete(parser_t* parser, rule_state_t* state)
{
  assert(parser != NULL);
  assert(state != NULL);

  process_deferred_ast(parser, state);

  if(state->scope && state->ast != NULL)
    ast_scope(state->ast);

  if(trace_enable)
    printf("Rule %s: Complete\n", state->fn_name);

  if(state->restart == NULL)
    return state->ast;

  // We have a restart point, check next token is legal
  token_id id = current_token_id(parser);

  if(trace_enable)
    printf("Rule %s: Check restart set for next token %s\n", state->fn_name,
      token_print(parser->token));

  for(const token_id* p = state->restart; *p != TK_NONE; p++)
  {
    if(*p == id)
    {
      // Legal token found
      if(trace_enable)
        printf("Rule %s: Restart check successful\n", state->fn_name);

      return state->ast;
    }
  }

  // Next token is not in restart set, error
  if(trace_enable)
    printf("Rule %s: Restart check error\n", state->fn_name);

  assert(parser->token != NULL);
  error(parser->source, token_line_number(parser->token),
    token_line_position(parser->token),
    "syntax error: unexpected token %s after %s", token_print(parser->token),
    state->desc);

  ast_free(state->ast);
  parser->failed = true;
  ditch_restart(parser, state);
  return NULL;
}
Beispiel #5
0
// Create a package AST, set up its state and add it to the given program
ast_t* create_package(ast_t* program, const char* name,
  const char* qualified_name, pass_opt_t* opt)
{
  ast_t* package = ast_blank(TK_PACKAGE);
  uint32_t pkg_id = program_assign_pkg_id(program);

  package_t* pkg = POOL_ALLOC(package_t);
  pkg->path = name;
  pkg->qualified_name = qualified_name;
  pkg->id = id_to_string(NULL, pkg_id);

  const char* p = strrchr(pkg->path, PATH_SLASH);

  if(p == NULL)
    p = pkg->path;
  else
    p = p + 1;

  pkg->filename = stringtab(p);

  if(pkg_id > 1)
    pkg->symbol = create_package_symbol(program, pkg->filename);
  else
    pkg->symbol = NULL;

  pkg->ast = package;
  package_set_init(&pkg->dependencies, 1);
  pkg->group = NULL;
  pkg->group_index = -1;
  pkg->next_hygienic_id = 0;
  pkg->low_index = -1;
  ast_setdata(package, pkg);

  ast_scope(package);
  ast_append(program, package);
  ast_set(program, pkg->path, package, SYM_NONE, false);
  ast_set(program, pkg->id, package, SYM_NONE, false);

  strlist_t* safe = opt->safe_packages;

  if((safe != NULL) && (strlist_find(safe, pkg->path) == NULL))
    pkg->allow_ffi = false;
  else
    pkg->allow_ffi = true;

  pkg->on_stack = false;

  return package;
}
Beispiel #6
0
/* Process a scope attribute.
 * The scope keyword should be found before calling this, but nothing else.
 */
static bool scope_attribute(build_parser_t* builder, ast_t* node)
{
  assert(builder != NULL);

  ast_scope(node);

  while(peek_token(builder) == AT_ID)
  {
    get_token(builder);
    const char* name = stringtab(token_string(builder->token));
    add_subtree_ref(builder, node, name, name);
  }

  return true;
}
Beispiel #7
0
void ast_clear(ast_t* ast)
{
  if(ast->symtab != NULL)
  {
    symtab_free(ast->symtab);
    ast_scope(ast);
  }

  ast_t* child = ast->child;

  while(child != NULL)
  {
    ast_clear(child);
    child = child->sibling;
  }
}
Beispiel #8
0
ast_t* program_load(const char* path, pass_opt_t* options)
{
  ast_t* program = ast_blank(TK_PROGRAM);
  ast_scope(program);

  options->type_catchup_pass = PASS_PARSE;

  if(package_load(program, path, options) == NULL ||
    !ast_passes_program(program, options))
  {
    ast_free(program);
    return NULL;
  }

  return program;
}
Beispiel #9
0
ast_t* program_load(const char* path, pass_opt_t* options)
{
  ast_t* program = ast_blank(TK_PROGRAM);
  ast_scope(program);

  if(package_load(program, path, options) == NULL)
  {
    ast_free(program);
    return NULL;
  }

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

  return program;
}
Beispiel #10
0
// Create a package AST, set up its state and add it to the given program
static ast_t* create_package(ast_t* program, const char* name)
{
  ast_t* package = ast_blank(TK_PACKAGE);
  uint32_t pkg_id = program_assign_pkg_id(program);

  package_t* pkg = POOL_ALLOC(package_t);
  pkg->path = name;
  pkg->id = id_to_string(NULL, pkg_id);

  const char* p = strrchr(pkg->path, PATH_SLASH);

  if(p == NULL)
    p = pkg->path;
  else
    p = p + 1;

  pkg->filename = stringtab(p);

  if(pkg_id > 1)
    pkg->symbol = create_package_symbol(program, pkg->filename);
  else
    pkg->symbol = NULL;

  pkg->next_hygienic_id = 0;
  ast_setdata(package, pkg);

  ast_scope(package);
  ast_append(program, package);
  ast_set(program, pkg->path, package, SYM_NONE);
  ast_set(program, pkg->id, package, SYM_NONE);

  if((safe != NULL) && (strlist_find(safe, pkg->path) == NULL))
    pkg->allow_ffi = false;
  else
    pkg->allow_ffi = true;

  return package;
}
Beispiel #11
0
// Handle a set of case methods, starting at the given method.
// Generate wrapper and worker methods and append them to the given list.
// Returns: true on success, false on failure.
static bool sugar_case_method(ast_t* first_case_method, ast_t* members,
  const char* name, typecheck_t* t)
{
  assert(first_case_method != NULL);
  assert(members != NULL);
  assert(name != NULL);
  assert(t != NULL);

  ast_t* wrapper = make_match_wrapper(first_case_method);

  if(wrapper == NULL)
    return false;

  ast_t* match_cases = ast_from(first_case_method, TK_CASES);
  ast_scope(match_cases);

  // Process all methods with the same name.
  // We need to remove processed methods. However, removing nodes from a list
  // we're iterating over gets rather complex. Instead we mark nodes and remove
  // them all in one sweep later.
  for(ast_t* p = first_case_method; p != NULL; p = ast_sibling(p))
  {
    const char* p_name = ast_name(ast_childidx(p, 1));

    if(p_name == name)
    {
      // This method is in the case set.
      ast_t* case_ast = add_case_method(wrapper, p);

      if(case_ast == NULL)
      {
        ast_free(wrapper);
        ast_free(match_cases);
        return false;
      }

      ast_append(match_cases, case_ast);
      ast_setid(p, TK_NONE);  // Mark for removal.
    }
  }

  // Check params and build match operand, worker parameters and worker call
  // arguments.
  ast_t* match_params = ast_childidx(wrapper, 3);
  ast_t* worker_params = ast_from(match_params, TK_PARAMS);
  ast_t* call_args = ast_from(match_params, TK_POSITIONALARGS);
  ast_t* match_operand = build_params(match_params, worker_params, call_args,
    name, t);

  if(match_operand == NULL)
  {
    ast_free(wrapper);
    ast_free(match_cases);
    ast_free(worker_params);
    ast_free(call_args);
    return false;
  }

  // Complete wrapper function and add to containing entity.
  const char* worker_name = package_hygienic_id(t);
  ast_t* wrapper_body = ast_childidx(wrapper, 6);
  ast_t* wrapper_call;
  ast_t* call_t_args = ast_from(wrapper, TK_NONE);
  assert(ast_id(wrapper_body) == TK_SEQ);
  assert(ast_childcount(wrapper_body) == 0);

  build_t_params(ast_childidx(wrapper, 2), call_t_args);

  if(ast_id(call_t_args) == TK_NONE)
  {
    // No type args needed.
    ast_free(call_t_args);
    BUILD(tmp, wrapper_body,
      NODE(TK_CALL,
        TREE(call_args)
        NONE
        NODE(TK_REFERENCE, ID(worker_name))));

    wrapper_call = tmp;
  }
  else
  {
    // Type args needed on call.
    BUILD(tmp, wrapper_body,
      NODE(TK_CALL,
        TREE(call_args)
        NONE
        NODE(TK_QUALIFY,
          NODE(TK_REFERENCE, ID(worker_name))
          TREE(call_t_args))));

    wrapper_call = tmp;
  }

  ast_append(wrapper_body, wrapper_call);
  ast_append(members, wrapper);

  // Make worker function and add to containing entity.
  AST_GET_CHILDREN(wrapper, cap, wrapper_id, t_params, wrapper_params,
    ret_type, error);

  if(ast_id(wrapper) == TK_BE)
    cap = ast_from(cap, TK_REF);

  BUILD(worker, wrapper,
    NODE(TK_FUN, AST_SCOPE
      TREE(cap)
      ID(worker_name)
      TREE(t_params)
      TREE(worker_params)
      TREE(ret_type)
      TREE(error)
      NODE(TK_SEQ,
        NODE(TK_MATCH, AST_SCOPE
          NODE(TK_SEQ, TREE(match_operand))
          TREE(match_cases)
          NONE)) // Else clause.
      NONE    // Doc string.
      NONE)); // Guard.

  ast_append(members, worker);
  return true;
}