Skip to content

Commit

Permalink
Add errno to stdlib (#368)
Browse files Browse the repository at this point in the history
  • Loading branch information
Akuli authored Mar 26, 2023
1 parent 5273af6 commit 0ff575e
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 23 deletions.
8 changes: 6 additions & 2 deletions self_hosted/ast.jou
Original file line number Diff line number Diff line change
Expand Up @@ -709,9 +709,11 @@ class AstFile:

# Iterating over imports:
# imp: AstImport* = NULL
# while file->next_import(&imp):
# while file->next_import(&imp, NULL):
# ...
def next_import(self, imp: AstImport**) -> bool:
#
# If imp_location is given, it is used to store the location of each import statement.
def next_import(self, imp: AstImport**, imp_location: Location*) -> bool:
# Get the corresponding AstToplevelStatement.
ts = *imp as AstToplevelStatement*
assert &ts->the_import as void* == ts # TODO: offsetof() or similar
Expand All @@ -725,6 +727,8 @@ class AstFile:
if ts == &self->body[self->body_len] or ts->kind != AstToplevelStatementKind::Import:
return False
*imp = &ts->the_import
if imp_location != NULL:
*imp_location = ts->location
return True

def print(self) -> void:
Expand Down
48 changes: 32 additions & 16 deletions self_hosted/main.jou
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import "../config.jou"
import "./ast.jou"
import "./errors_and_warnings.jou"
import "./tokenizer.jou"
import "./parser.jou"
import "./types.jou"
Expand Down Expand Up @@ -121,6 +122,11 @@ class FileState:
typectx: FileTypes
pending_exports: ExportSymbol*

class ParseQueueItem:
path: byte*
is_imported: bool
import_location: Location

class Compiler:
argv0: byte*
verbosity: int
Expand All @@ -131,33 +137,37 @@ class Compiler:
automagic_files: byte*[10]

def determine_automagic_files(self) -> void:
# TODO: this breaks too much stuff
return
# self->automagic_files[0] = malloc(strlen(self->stdlib_path) + 40)
# sprintf(self->automagic_files[0], "%s/_assert_fail.jou", self->stdlib_path)
self->automagic_files[0] = malloc(strlen(self->stdlib_path) + 40)
sprintf(self->automagic_files[0], "%s/_assert_fail.jou", self->stdlib_path)
if is_windows():
self->automagic_files[1] = malloc(strlen(self->stdlib_path) + 40)
sprintf(self->automagic_files[1], "%s/_windows_startup.jou", self->stdlib_path)

def parse_all_files(self) -> void:
queue: byte** = malloc(50 * sizeof queue[0])
queue: ParseQueueItem* = malloc(50 * sizeof queue[0])
queue_len = 0
queue[queue_len++] = self->args->main_path
queue[queue_len++] = ParseQueueItem{path = self->args->main_path}
for i = 0; self->automagic_files[i] != NULL; i++:
queue[queue_len++] = self->automagic_files[i]
queue[queue_len++] = ParseQueueItem{path = self->automagic_files[i]}

while queue_len > 0:
path = queue[--queue_len]
item = queue[--queue_len]

found = False
for i = 0; i < self->nfiles; i++:
if strcmp(self->files[i].ast.path, path) == 0:
if strcmp(self->files[i].ast.path, item.path) == 0:
found = True
break
if found:
continue

if self->verbosity >= 1:
printf("Parsing %s\n", path)
printf("Parsing %s\n", item.path)

tokens = tokenize(path)
if item.is_imported:
tokens = tokenize(item.path, &item.import_location)
else:
tokens = tokenize(item.path, NULL)
if self->verbosity >= 2:
print_tokens(tokens)
ast = parse(tokens, self->stdlib_path)
Expand All @@ -169,9 +179,15 @@ class Compiler:
self->files[self->nfiles++] = FileState{ast = ast}

imp: AstImport* = NULL
while ast.next_import(&imp):
imp_location: Location
while ast.next_import(&imp, &imp_location):
# TODO: offsetof()
queue = realloc(queue, sizeof queue[0] * (queue_len + 1))
queue[queue_len++] = imp->resolved_path
queue[queue_len++] = ParseQueueItem{
path = imp->resolved_path,
is_imported = True,
import_location = imp_location,
}

free(queue)

Expand All @@ -185,7 +201,7 @@ class Compiler:
dest = &self->files[idest]

imp: AstImport* = NULL
while dest->ast.next_import(&imp):
while dest->ast.next_import(&imp, NULL):
for exp = src->pending_exports; exp->name[0] != '\0'; exp++:
if self->verbosity >= 1:
printf(
Expand Down Expand Up @@ -369,11 +385,11 @@ def main(argc: int, argv: byte**) -> int:
args = parse_args(argc, argv)

if args.mode == CompilerMode::TokenizeOnly:
tokens = tokenize(args.main_path)
tokens = tokenize(args.main_path, NULL)
print_tokens(tokens)
free(tokens)
elif args.mode == CompilerMode::ParseOnly:
tokens = tokenize(args.main_path)
tokens = tokenize(args.main_path, NULL)
stdlib_path = find_stdlib()
ast = parse(tokens, stdlib_path)
ast.print()
Expand Down
3 changes: 2 additions & 1 deletion self_hosted/runs_wrong.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ stdlib/mem.jou
stdlib/process.jou
stdlib/str.jou
stdlib/_windows_startup.jou
tests/404/file.jou
tests/404/method_on_class_ptr.jou
tests/404/method_on_int.jou
tests/already_exists_error/class_import.jou
Expand Down Expand Up @@ -72,3 +71,5 @@ tests/should_succeed/union.jou
tests/other_errors/instantiation_address_of_field.jou
tests/other_errors/array_literal_as_a_pointer.jou
tests/other_errors/assert_fail_multiline.jou
stdlib/errno.jou
tests/should_succeed/errno_test.jou
14 changes: 10 additions & 4 deletions self_hosted/tokenizer.jou
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import "stdlib/io.jou"
import "stdlib/str.jou"
import "stdlib/mem.jou"
import "stdlib/errno.jou"
import "./errors_and_warnings.jou"
import "./token.jou"

Expand Down Expand Up @@ -587,12 +588,17 @@ def handle_indentations(raw_tokens: Token*) -> Token*:
return tokens


def tokenize(path: byte*) -> Token*:
def tokenize(path: byte*, import_location: Location*) -> Token*:
file = fopen(path, "rb")
if file == NULL:
# TODO: test this
# TODO: include errno in the message
fail(Location{path=path}, "cannot open file")
message: byte[200]
if import_location == NULL:
# File is not imported
snprintf(message, sizeof message, "cannot open file: %s", strerror(get_errno()))
fail(Location{path=path}, message)
else:
snprintf(message, sizeof message, "cannot import from \"%s\": %s", path, strerror(get_errno()))
fail(*import_location, message)

raw_tokens = tokenize_without_indent_dedent_tokens(file, path)
better_tokens = handle_indentations(raw_tokens)
Expand Down
6 changes: 6 additions & 0 deletions stdlib/_windows_startup.jou
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,9 @@ def _jou_windows_startup() -> void:
stdin = __acrt_iob_func(0)
stdout = __acrt_iob_func(1)
stderr = __acrt_iob_func(2)

# On linux, C's errno is a macro that expands to (*__errno_location()).
# On Windows it expands to (*_errno()) instead. Let's make it consistent.
declare _errno() -> int*
def __errno_location() -> int*:
return _errno()
12 changes: 12 additions & 0 deletions stdlib/errno.jou
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# C's errno is actually a macro that expands to a function call.
# See also _windows_startup.jou
declare __errno_location() -> int*

def set_errno(value: int) -> void:
*__errno_location() = value

def get_errno() -> int:
return *__errno_location()

# Convert an error code into a string. Do not modify or free() the returned string.
declare strerror(errno_value: int) -> byte*
17 changes: 17 additions & 0 deletions tests/should_succeed/errno_test.jou
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import "stdlib/io.jou"
import "stdlib/errno.jou"

def main() -> int:
# Avoid printing strerror(0) because it is platform-specific
printf("%d\n", get_errno()) # Output: 0

fopen("this does not exist.txt", "r")
printf("%d %s\n", get_errno(), strerror(get_errno())) # Output: 2 No such file or directory

set_errno(0)
printf("%d\n", get_errno()) # Output: 0

set_errno(2)
printf("%d %s\n", get_errno(), strerror(get_errno())) # Output: 2 No such file or directory

return 0

0 comments on commit 0ff575e

Please sign in to comment.