Skip to content

Commit

Permalink
Add setjmp for error handling in Parser; misc API changes
Browse files Browse the repository at this point in the history
Before we would just exit early in certain parsing cases where we
did not know how to handle errors better. Instead, we not `setjmp`
to the entry-point of the parser and return back when we encounter
unexpected issues instead of just exiting.
  • Loading branch information
mustafaquraish committed Mar 31, 2024
1 parent 0ffba9a commit 7d8702c
Show file tree
Hide file tree
Showing 10 changed files with 62 additions and 29 deletions.
39 changes: 24 additions & 15 deletions bootstrap/stage0.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "unistd.h"
#include "sys/stat.h"
#include "sys/types.h"
#include "setjmp.h"
#include "dirent.h"
#include "libgen.h"

Expand Down Expand Up @@ -834,6 +835,7 @@ struct compiler_ast_program_Program {
std_vector_Vector__12 *errors;
u32 error_level;
compiler_ast_program_CachedTypes cached_types;
jmp_buf err_jmp_ctx;
};

struct compiler_ast_program_NSIterator {
Expand Down Expand Up @@ -1770,7 +1772,7 @@ char *compiler_parser_Parser_find_external_library_path(compiler_parser_Parser *
compiler_ast_program_Namespace *compiler_parser_Parser_find_external_library(compiler_parser_Parser *this, char *name);
bool compiler_parser_Parser_load_import_path(compiler_parser_Parser *this, compiler_ast_nodes_AST *import_stmt);
void compiler_parser_Parser_load_file(compiler_parser_Parser *this, char *filename, char *contents);
void compiler_parser_couldnt_find_stdlib(void);
void compiler_parser_Parser_couldnt_find_stdlib(compiler_parser_Parser *this);
void compiler_parser_Parser_find_and_import_stdlib(compiler_parser_Parser *this);
void compiler_parser_Parser_include_prelude_only(compiler_parser_Parser *this);
void compiler_parser_Parser_create_namespaces_for_initial_file(compiler_parser_Parser *this, char *filename, bool single_file);
Expand Down Expand Up @@ -6181,8 +6183,8 @@ void compiler_passes_typechecker_TypeChecker_try_resolve_typedefs_in_namespace(c
continue;

compiler_ast_scopes_Symbol *sym = compiler_ast_scopes_Scope_lookup_recursive(compiler_passes_generic_pass_GenericPass_scope(this->o), it->key);
ae_assert(((bool)sym), "/Users/mustafa/ocen-lang/ocen/compiler/passes/typechecker.oc:2051:16: Assertion failed: `sym?`", "Should have added the symbol into scope already");
ae_assert(sym->type==compiler_ast_scopes_SymbolType_TypeDef, "/Users/mustafa/ocen-lang/ocen/compiler/passes/typechecker.oc:2055:16: Assertion failed: `sym.type == TypeDef`", NULL);
ae_assert(((bool)sym), "/Users/mustafa/ocen-lang/ocen/compiler/passes/typechecker.oc:2050:16: Assertion failed: `sym?`", "Should have added the symbol into scope already");
ae_assert(sym->type==compiler_ast_scopes_SymbolType_TypeDef, "/Users/mustafa/ocen-lang/ocen/compiler/passes/typechecker.oc:2054:16: Assertion failed: `sym.type == TypeDef`", NULL);
compiler_types_Type *res = compiler_passes_typechecker_TypeChecker_resolve_type(this, it->value, false, !pre_import, true);
if (!((bool)res))
continue;
Expand Down Expand Up @@ -7032,8 +7034,9 @@ void compiler_parser_Parser_unhandled_type(compiler_parser_Parser *this, char *f

compiler_tokens_Token *compiler_parser_Parser_token(compiler_parser_Parser *this) {
if ((this->curr >= this->tokens->size)) {
this->curr=(this->tokens->size - 1);
compiler_parser_Parser_error_msg(this, "Unexpected end of file");
compiler_ast_program_Program_exit_with_errors(this->program);
longjmp(this->program->err_jmp_ctx, 1);
}
return std_vector_Vector__10_unchecked_at(this->tokens, ((u32)this->curr));
}
Expand All @@ -7056,7 +7059,9 @@ bool compiler_parser_Parser_peek_token_is(compiler_parser_Parser *this, u32 off,
bool compiler_parser_Parser_consume_if(compiler_parser_Parser *this, compiler_tokens_TokenType type) {
if (compiler_parser_Parser_token_is(this, type)) {
if ((type != compiler_tokens_TokenType_Newline)) {
if ((this->curr < (this->tokens->size - 1)))
this->curr+=1;

}
return true;
}
Expand All @@ -7065,10 +7070,12 @@ bool compiler_parser_Parser_consume_if(compiler_parser_Parser *this, compiler_to

void compiler_parser_Parser_consume_newline_or(compiler_parser_Parser *this, compiler_tokens_TokenType type) {
if (compiler_parser_Parser_token_is(this, type)) {
if ((this->curr < (this->tokens->size - 1)))
this->curr+=1;

} else if (!compiler_parser_Parser_token(this)->seen_newline) {
compiler_parser_Parser_error_msg(this, format_string("Expected %s or newline", compiler_tokens_TokenType_str(type)));
compiler_ast_program_Program_exit_with_errors(this->program);
longjmp(this->program->err_jmp_ctx, 1);
}

}
Expand All @@ -7077,7 +7084,7 @@ compiler_tokens_Token *compiler_parser_Parser_consume(compiler_parser_Parser *th
compiler_tokens_Token *tok = compiler_parser_Parser_token(this);
if (!compiler_parser_Parser_consume_if(this, type)) {
compiler_parser_Parser_error_msg(this, format_string("Expected TokenType::%s", compiler_tokens_TokenType_str(type)));
compiler_ast_program_Program_exit_with_errors(this->program);
longjmp(this->program->err_jmp_ctx, 1);
}
return tok;
}
Expand Down Expand Up @@ -8313,7 +8320,7 @@ void compiler_parser_Parser_parse_extern_into_symbol(compiler_parser_Parser *thi
}

void compiler_parser_Parser_get_extern_from_attr(compiler_parser_Parser *this, compiler_ast_scopes_Symbol *sym, compiler_attributes_Attribute *attr) {
ae_assert(attr->type==compiler_attributes_AttributeType_Extern, "/Users/mustafa/ocen-lang/ocen/compiler/parser.oc:1420:12: Assertion failed: `attr.type == Extern`", NULL);
ae_assert(attr->type==compiler_attributes_AttributeType_Extern, "/Users/mustafa/ocen-lang/ocen/compiler/parser.oc:1422:12: Assertion failed: `attr.type == Extern`", NULL);
sym->is_extern=true;
if ((attr->args->size > 0)) {
sym->extern_name=std_vector_Vector__1_at(attr->args, 0);
Expand Down Expand Up @@ -8849,8 +8856,8 @@ bool compiler_parser_Parser_load_import_path(compiler_parser_Parser *this, compi
switch (path->type) {
case compiler_ast_nodes_ImportType_GlobalNamespace: {
std_vector_Vector__5 *parts = path->parts;
ae_assert((parts->size > 0), "/Users/mustafa/ocen-lang/ocen/compiler/parser.oc:1976:20: Assertion failed: `parts.size > 0`", "Expected at least one part in import path");
ae_assert(std_vector_Vector__5_at(parts, 0)->type==compiler_ast_nodes_ImportPartType_Single, "/Users/mustafa/ocen-lang/ocen/compiler/parser.oc:1977:20: Assertion failed: `parts.at(0).type == Single`", "Expected first part to be a single import");
ae_assert((parts->size > 0), "/Users/mustafa/ocen-lang/ocen/compiler/parser.oc:1978:20: Assertion failed: `parts.size > 0`", "Expected at least one part in import path");
ae_assert(std_vector_Vector__5_at(parts, 0)->type==compiler_ast_nodes_ImportPartType_Single, "/Users/mustafa/ocen-lang/ocen/compiler/parser.oc:1979:20: Assertion failed: `parts.at(0).type == Single`", "Expected first part to be a single import");
compiler_ast_nodes_ImportPartSingle first_part = std_vector_Vector__5_at(parts, 0)->u.single;
char *lib_name = first_part.name;
if (!std_map_Map__4_contains(this->program->global->namespaces, lib_name)) {
Expand All @@ -8872,7 +8879,7 @@ bool compiler_parser_Parser_load_import_path(compiler_parser_Parser *this, compi
if (!((bool)cur->parent)) {
compiler_ast_nodes_ImportPart *first_part = std_vector_Vector__5_at(path->parts, 0);
compiler_parser_Parser_error(this, compiler_errors_Error_new(first_part->span, "Cannot import from parent of root namespace"));
compiler_ast_program_Program_exit_with_errors(this->program);
longjmp(this->program->err_jmp_ctx, 1);
}
cur=cur->parent;
}
Expand Down Expand Up @@ -8912,12 +8919,12 @@ void compiler_parser_Parser_load_file(compiler_parser_Parser *this, char *filena
this->ns->span=std_span_Span_join(start, end);
}

void compiler_parser_couldnt_find_stdlib(void) {
void compiler_parser_Parser_couldnt_find_stdlib(compiler_parser_Parser *this) {
printf("--------------------------------------------------------------------------------""\n");
printf(" Could not find standard library. Set OCEN_ROOT environment variable.""\n");
printf(" Alternatively, compile from the root of `ocen` repository.""\n");
printf("--------------------------------------------------------------------------------""\n");
exit(1);
longjmp(this->program->err_jmp_ctx, 1);
}

void compiler_parser_Parser_find_and_import_stdlib(compiler_parser_Parser *this) {
Expand All @@ -8928,12 +8935,11 @@ void compiler_parser_Parser_find_and_import_stdlib(compiler_parser_Parser *this)
void compiler_parser_Parser_include_prelude_only(compiler_parser_Parser *this) {
char *std_path = compiler_parser_Parser_find_external_library_path(this, "std");
if (!((bool)std_path)) {
compiler_parser_couldnt_find_stdlib();
compiler_parser_Parser_couldnt_find_stdlib(this);
}
compiler_parser_Parser_consume_end_of_statement(this);
char *prelude_path = format_string("%s/prelude.h", std_path);
if (!std_fs_file_exists(prelude_path)) {
compiler_parser_couldnt_find_stdlib();
compiler_parser_Parser_couldnt_find_stdlib(this);
}
std_buffer_Buffer content = std_fs_read_file(prelude_path);
std_map_Map__6_insert(this->program->c_embeds, prelude_path, std_buffer_Buffer_str(content));
Expand Down Expand Up @@ -8998,6 +9004,9 @@ void compiler_parser_Parser_create_namespaces_for_initial_file(compiler_parser_P
}

void compiler_parser_Parser_parse_toplevel(compiler_ast_program_Program *program, char *filename, bool include_stdlib, char *file_contents) {
if ((setjmp(program->err_jmp_ctx) > 0))
return ;

compiler_parser_Parser parser = compiler_parser_Parser_make(program, program->global);
if (include_stdlib) {
compiler_parser_Parser_find_and_import_stdlib(&parser);
Expand Down
1 change: 0 additions & 1 deletion compiler/ast/nodes.oc
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,6 @@ def AST::is_identifier(&this): bool => match .type {
def AST::symbol(&this): &Symbol => .resolved_symbol

def AST::is_lvalue(&this): bool => match .type {
// FIXME: What about `a::b::c?`
Identifier => not .u.ident.is_function
Member => true
Dereference => true
Expand Down
2 changes: 2 additions & 0 deletions compiler/ast/program.oc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import std::vector::Vector
import std::span::Span
import std::map::Map
import std::libc::{ calloc, exit }
import std::setjmp::{ ErrorContext }
import @ast::scopes::{ Symbol, Scope }
import @ast::nodes::{ AST, Function, Structure, Enum }
import @errors::Error
Expand Down Expand Up @@ -168,6 +169,7 @@ struct Program {
error_level: u32

cached_types: CachedTypes
err_jmp_ctx: ErrorContext
}

def Program::new(): &Program {
Expand Down
24 changes: 14 additions & 10 deletions compiler/parser.oc
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,10 @@ def Parser::unhandled_type(&this, func: str) {

def Parser::token(&this): &Token {
if .curr >= .tokens.size {
.curr = .tokens.size - 1
// If we run out of tokens without an error, report one.
.error_msg("Unexpected end of file")
.program.exit_with_errors()
.program.err_jmp_ctx.jump_back(1)
}
return .tokens.unchecked_at(.curr as u32)
}
Expand All @@ -89,7 +90,7 @@ def Parser::consume_if(&this, type: TokenType): bool {
if .token_is(type) {
// Newline tokens are special because they don't consume a token.
if type != TokenType::Newline {
.curr += 1
if .curr < .tokens.size - 1 then .curr += 1
}
return true
}
Expand All @@ -98,18 +99,19 @@ def Parser::consume_if(&this, type: TokenType): bool {

def Parser::consume_newline_or(&this, type: TokenType) {
if .token_is(type) {
.curr += 1
if .curr < .tokens.size - 1 then .curr += 1

} else if not .token().seen_newline {
.error_msg(`Expected {type.str()} or newline`)
.program.exit_with_errors()
.program.err_jmp_ctx.jump_back(1)
}
}

def Parser::consume(&this, type: TokenType): &Token {
let tok = .token()
if not .consume_if(type) {
.error_msg(`Expected TokenType::{type.str()}`)
.program.exit_with_errors()
.program.err_jmp_ctx.jump_back(1)
}
return tok
}
Expand Down Expand Up @@ -1998,7 +2000,7 @@ def Parser::load_import_path(&this, import_stmt: &AST): bool {
if not cur.parent? {
let first_part = path.parts.at(0)
.error(Error::new(first_part.span, "Cannot import from parent of root namespace"))
.program.exit_with_errors()
.program.err_jmp_ctx.jump_back(1)
}
cur = cur.parent
}
Expand Down Expand Up @@ -2042,12 +2044,12 @@ def Parser::load_file(&this, filename: str, contents: str = null) {
.ns.span = start.join(end)
}

def couldnt_find_stdlib() {
def Parser::couldnt_find_stdlib(&this) {
println("--------------------------------------------------------------------------------")
println(" Could not find standard library. Set OCEN_ROOT environment variable.")
println(" Alternatively, compile from the root of `ocen` repository.")
println("--------------------------------------------------------------------------------")
std::exit(1)
.program.err_jmp_ctx.jump_back(1)
}

def Parser::find_and_import_stdlib(&this) {
Expand All @@ -2059,11 +2061,11 @@ def Parser::include_prelude_only(&this) {
// Want to manually include the prelude
let std_path = .find_external_library_path("std")
if not std_path? {
couldnt_find_stdlib()
.couldnt_find_stdlib()
}
let prelude_path = f"{std_path}/prelude.h"
if not fs::file_exists(prelude_path) {
couldnt_find_stdlib()
.couldnt_find_stdlib()
}
let content = fs::read_file(prelude_path)
.program.c_embeds.insert(prelude_path, content.str())
Expand Down Expand Up @@ -2175,6 +2177,8 @@ def Parser::create_namespaces_for_initial_file(&this, filename: str, single_file
}

def Parser::parse_toplevel(program: &Program, filename: str, include_stdlib: bool, file_contents: str = null) {
if program.err_jmp_ctx.set_jump_point() > 0 return

let parser = Parser::make(program, program.global)
if include_stdlib {
parser.find_and_import_stdlib()
Expand Down
1 change: 0 additions & 1 deletion compiler/passes/typechecker.oc
Original file line number Diff line number Diff line change
Expand Up @@ -1428,7 +1428,6 @@ def TypeChecker::check_for(&this, node: &AST) {
def TypeChecker::check_statement(&this, node: &AST) {
match node.type {
ASTType::Return | ASTType::ArrowReturn => {
// todo: make sure in function
let cur_func = .scope().cur_func
if not cur_func? {
.error(Error::new(node.span, "Cannot return from outside a function"))
Expand Down
2 changes: 1 addition & 1 deletion meta/gen_docs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ cat > compiler/doc.oc << EOF
//* Nothing to see here
import std::{ vector, map, compact_map, heap, deque, set, linkedlist, variadic, bencode }
import std::{ buffer, bufferio, image, json, math, complex, fft, random, curl, og }
import std::{ buffer, bufferio, image, json, math, complex, fft, random, curl, og, setjmp }
import std::{ value, vec, glut, socket, sort, thread, video_renderer, sv, fs, zlib, time }
import std::traits::{ hash, eq, compare }
import std::{ image::draw, hash::sha1, hash::sha256 }
Expand Down
1 change: 0 additions & 1 deletion std/heap.oc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import std::vector::Vector
import std::traits::compare
import std::libc::{ calloc, free }


enum Mode {
Min
Max
Expand Down
12 changes: 12 additions & 0 deletions std/setjmp.oc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

@compiler c_include "setjmp.h"

[extern "jmp_buf"]
struct ErrorContext

[extern "setjmp"]
def ErrorContext::set_jump_point(this): i32

[exits]
[extern "longjmp"]
def ErrorContext::jump_back(this, status: i32)
5 changes: 5 additions & 0 deletions std/value.oc
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ def Value::push(&this, value: &Value) {
.u.as_list.push(value)
}

def Value::contains(&this, key: str): bool {
if .type != Dictionary return false
return .u.as_dict.contains(key)
}

def Value::get(&this, key: str, defolt: &Value = null): &Value {
.ensure(Dictionary)
return .u.as_dict.get(key, defolt)
Expand Down
4 changes: 4 additions & 0 deletions tests/bad/missing_curlies.oc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/// fail: Unexpected end of file

def main() {
println("Hello, World!\n")

0 comments on commit 7d8702c

Please sign in to comment.