// Ditch tokens until a legal one is found // The token set must be TK_NONE terminated static void ditch_restart(parser_t* parser, rule_state_t* state) { assert(parser != NULL); assert(state != NULL); assert(state->restart != NULL); if(trace_enable) printf("Rule %s: Attempting recovery:\n", state->fn_name); while(true) { token_id id = current_token_id(parser); for(const token_id* p = state->restart; *p != TK_NONE; p++) { if(*p == id) { // Legal token found if(trace_enable) printf(" recovered with %s\n", token_print(parser->token)); return; } } // Current token is not in legal set, ditch it if(trace_enable) printf(" ignoring %d %s %s\n", id, lexer_print(id), token_print(parser->token)); consume_token_no_ast(parser); } }
const char* genname_fun(token_id cap, const char* name, ast_t* typeargs) { // cap_name[_Arg1_Arg2] printbuf_t* buf = printbuf_new(); printbuf(buf, "%s_%s", lexer_print(cap), name); types_append(buf, typeargs); return stringtab_buf(buf); }
const char* token_print(token_t* token) { assert(token != NULL); switch(token->id) { case TK_EOF: return "EOF"; case TK_ID: case TK_STRING: return token->string; case TK_INT: if (token->printed == NULL) token->printed = (char*)pool_alloc_size(64); snprintf(token->printed, 64, __zu, (size_t)token->integer); return token->printed; case TK_FLOAT: { if(token->printed == NULL) token->printed = (char*)pool_alloc_size(64); int r = snprintf(token->printed, 64, "%g", token->real); if(strcspn(token->printed, ".e") == (size_t)r) snprintf(token->printed + r, 64 - r, ".0"); return token->printed; } case TK_LEX_ERROR: return "LEX_ERROR"; default: break; } const char* p = lexer_print(token->id); if(p != NULL) return p; if(token->printed == NULL) token->printed = (char*)pool_alloc_size(64); snprintf(token->printed, 64, "Unknown_token_%d", token->id); return token->printed; }
const char* token_id_desc(token_id id) { switch(id) { case TK_EOF: return "EOF"; case TK_ID: return "id"; case TK_STRING: return "string literal"; case TK_INT: return "int literal"; case TK_FLOAT: return "float literal"; case TK_TRUE: return "true literal"; case TK_FALSE: return "false literal"; default: break; } const char* p = lexer_print(id); if(p != NULL) return p; return "UNKOWN"; }
// Build a list of token references with the given node static void bnf_token_set(bnf_t* bnf, token_id* tokens, bool clean) { pony_assert(bnf != NULL); for(int i = 0; tokens[i] != TK_NONE; i++) { bnf_t* p = bnf_add(bnf_create(BNF_TOKEN), bnf); pony_assert(p != NULL); //token_id next = tokens[i + 1]; switch(tokens[i]) { // Special case tokens case TK_EOF: p->name = ""; break; case TK_STRING: p->name = "STRING"; break; case TK_INT: p->name = "INT"; break; case TK_FLOAT: p->name = "FLOAT"; break; case TK_ID: p->name = "ID"; break; case TK_LPAREN_NEW: p->name = "LPAREN_NEW"; break; case TK_LSQUARE_NEW: p->name = "LSQUARE_NEW"; break; case TK_MINUS_NEW: p->name = "MINUS_NEW"; break; case TK_MINUS_TILDE_NEW: p->name = "MINUS_TILDE_NEW"; break; default: // Fixed text tokens: keywords, symbols, etc p->name = lexer_print(tokens[i]); p->id = BNF_QUOTED_TOKEN; pony_assert(p->name != NULL); if((clean && p->name[0] == '$') || tokens[i] == TK_NEWLINE) { // Remove unclean symbol p->id = BNF_NEVER; } break; } } }
bool expr_assign(pass_opt_t* opt, ast_t* ast) { // Left and right are swapped in the AST to make sure we type check the // right side before the left. Fetch them in the opposite order. assert(ast != NULL); AST_GET_CHILDREN(ast, right, left); ast_t* l_type = ast_type(left); if(!is_lvalue(&opt->check, left, is_result_needed(ast))) { ast_error(ast, "left side must be something that can be assigned to"); return false; } assert(l_type != NULL); if(!coerce_literals(&right, l_type, opt)) return false; ast_t* r_type = ast_type(right); if(is_typecheck_error(r_type)) return false; if(!infer_locals(left, r_type)) return false; // Inferring locals may have changed the left type. l_type = ast_type(left); // Assignment is based on the alias of the right hand side. ast_t* a_type = alias(r_type); if(!is_subtype(a_type, l_type)) { ast_error(ast, "right side must be a subtype of left side"); ast_error(a_type, "right side type: %s", ast_print_type(a_type)); ast_error(l_type, "left side type: %s", ast_print_type(l_type)); ast_free_unattached(a_type); return false; } if((ast_id(left) == TK_TUPLE) && (ast_id(a_type) != TK_TUPLETYPE)) { switch(ast_id(a_type)) { case TK_UNIONTYPE: ast_error(ast, "can't destructure a union using assignment, use pattern matching " "instead"); break; case TK_ISECTTYPE: ast_error(ast, "can't destructure an intersection using assignment, use pattern " "matching instead"); break; default: assert(0); break; } ast_error(a_type, "right side type: %s", ast_print_type(a_type)); ast_free_unattached(a_type); return false; } bool ok_safe = safe_to_write(left, a_type); if(!ok_safe) { if(ast_id(left) == TK_FVARREF && ast_child(left) != NULL && ast_id(ast_child(left)) == TK_THIS) { // We are writing to a field in this ast_t* fn = ast_nearest(left, TK_FUN); if(fn != NULL) { ast_t* iso = ast_child(fn); assert(iso != NULL); token_id iso_id = ast_id(iso); if(iso_id == TK_BOX || iso_id == TK_VAL || iso_id == TK_TAG) { ast_error(ast, "cannot write to a field in a %s function", lexer_print(iso_id)); ast_free_unattached(a_type); return false; } } } ast_error(ast, "not safe to write right side to left side"); ast_error(a_type, "right side type: %s", ast_print_type(a_type)); ast_free_unattached(a_type); return false; } ast_free_unattached(a_type); // If it's an embedded field, check for a constructor result. if(ast_id(left) == TK_EMBEDREF) { if((ast_id(right) != TK_CALL) || (ast_id(ast_childidx(right, 2)) != TK_NEWREF)) { ast_error(ast, "an embedded field must be assigned using a constructor"); return false; } } ast_settype(ast, consume_type(l_type, TK_NONE)); ast_inheritflags(ast); return true; }
bool expr_assign(pass_opt_t* opt, ast_t* ast) { // Left and right are swapped in the AST to make sure we type check the // right side before the left. Fetch them in the opposite order. assert(ast != NULL); AST_GET_CHILDREN(ast, right, left); ast_t* l_type = ast_type(left); if(l_type == NULL || !is_lvalue(opt, left, is_result_needed(ast))) { ast_error(opt->check.errors, ast, "left side must be something that can be assigned to"); return false; } if(!coerce_literals(&right, l_type, opt)) return false; ast_t* r_type = ast_type(right); if(is_typecheck_error(r_type)) return false; if(is_control_type(r_type)) { ast_error(opt->check.errors, ast, "the right hand side does not return a value"); return false; } if(!infer_locals(opt, left, r_type)) return false; // Inferring locals may have changed the left type. l_type = ast_type(left); // Assignment is based on the alias of the right hand side. ast_t* a_type = alias(r_type); errorframe_t info = NULL; if(!is_subtype(a_type, l_type, &info, opt)) { errorframe_t frame = NULL; ast_error_frame(&frame, ast, "right side must be a subtype of left side"); errorframe_append(&frame, &info); errorframe_report(&frame, opt->check.errors); ast_free_unattached(a_type); return false; } if((ast_id(left) == TK_TUPLE) && (ast_id(a_type) != TK_TUPLETYPE)) { switch(ast_id(a_type)) { case TK_UNIONTYPE: ast_error(opt->check.errors, ast, "can't destructure a union using assignment, use pattern matching " "instead"); break; case TK_ISECTTYPE: ast_error(opt->check.errors, ast, "can't destructure an intersection using assignment, use pattern " "matching instead"); break; default: assert(0); break; } ast_free_unattached(a_type); return false; } bool ok_safe = safe_to_write(left, a_type); if(!ok_safe) { if(ast_id(left) == TK_FVARREF && ast_child(left) != NULL && ast_id(ast_child(left)) == TK_THIS) { // We are writing to a field in this ast_t* fn = ast_nearest(left, TK_FUN); if(fn != NULL) { ast_t* iso = ast_child(fn); assert(iso != NULL); token_id iso_id = ast_id(iso); if(iso_id == TK_BOX || iso_id == TK_VAL || iso_id == TK_TAG) { ast_error(opt->check.errors, ast, "cannot write to a field in a %s function. If you are trying to " "change state in a function use fun ref", lexer_print(iso_id)); ast_free_unattached(a_type); return false; } } } ast_error(opt->check.errors, ast, "not safe to write right side to left side"); ast_error_continue(opt->check.errors, a_type, "right side type: %s", ast_print_type(a_type)); ast_free_unattached(a_type); return false; } ast_free_unattached(a_type); if(!check_embed_construction(opt, left, right)) return false; ast_settype(ast, consume_type(l_type, TK_NONE)); return true; }