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; }
void ast_clear_local(ast_t* ast) { if(ast->symtab != NULL) { symtab_free(ast->symtab); ast_scope(ast); } }
// 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); }
/* 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; }
// 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; }
/* 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; }
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; } }
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; }
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; }
// 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; }
// 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; }