From 981059e7452725be9b48bcf5bc3fb9a080e417a3 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Wed, 14 May 2025 23:05:52 +0200 Subject: [PATCH] Update to GCC 15.1 --- Rakefile | 6 +- boot/lexer.ll | 4 +- boot/parser.yy | 24 ++-- source.elna | 373 +++++++++++++++++++++++++++---------------------- tools/init.c | 204 --------------------------- 5 files changed, 218 insertions(+), 393 deletions(-) delete mode 100644 tools/init.c diff --git a/Rakefile b/Rakefile index 5d04bd5..d270857 100644 --- a/Rakefile +++ b/Rakefile @@ -8,8 +8,7 @@ require 'rake/clean' require_relative 'tools/support' # Dependencies. -GCC_VERSION = "14.2.0" -GCC_PATCH = 'https://raw.githubusercontent.com/Homebrew/formula-patches/f30c309442a60cfb926e780eae5d70571f8ab2cb/gcc/gcc-14.2.0-r2.diff' +GCC_VERSION = "15.1.0" # Paths. HOST_GCC = TMP + 'host/gcc' @@ -22,7 +21,7 @@ directory HOST_GCC directory HOST_INSTALL task default: [TMP + 'elna'] do - sh (TMP + 'elna').to_path, '--tokenize', 'source.elna' + sh (TMP + 'elna').to_path, '--lex', 'source.elna' end namespace :boot do @@ -34,7 +33,6 @@ namespace :boot do frontend_link = source_directory + 'gcc' download_and_pipe url, source_directory.dirname, ['tar', '-Jxv'] - download_and_pipe URI.parse(GCC_PATCH), source_directory, ['patch', '-p1'] sh 'contrib/download_prerequisites', chdir: source_directory.to_path File.symlink Pathname.new('.').relative_path_from(frontend_link), (frontend_link + 'elna') diff --git a/boot/lexer.ll b/boot/lexer.ll index 181d900..4e18376 100644 --- a/boot/lexer.ll +++ b/boot/lexer.ll @@ -128,8 +128,8 @@ return { break { return yy::parser::make_BREAK(this->location); } -repeat { - return yy::parser::make_REPEAT(this->location); +program { + return yy::parser::make_PROGRAM(this->location); } cast { return yy::parser::make_CAST(this->location); diff --git a/boot/parser.yy b/boot/parser.yy index 933c578..4f64e51 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -108,7 +108,7 @@ along with GCC; see the file COPYING3. If not see ELSE "else" ELSIF "elsif" RETURN "return" - REPEAT "repeat" + PROGRAM "program" BREAK "break" BEGIN_BLOCK "begin" END_BLOCK "end" @@ -166,15 +166,15 @@ along with GCC; see the file COPYING3. If not see %type > identifiers; %% program: - constant_part type_part variable_part procedure_part "begin" statements "end" "." + "program" constant_part type_part variable_part procedure_part "begin" statements "end" "." { - auto tree = new boot::program(boot::make_position(@5)); + auto tree = new boot::program(boot::make_position(@6)); - std::swap(tree->constants, $1); - std::swap(tree->types , $2); - std::swap(tree->variables, $3); - std::swap(tree->procedures, $4); - std::swap(tree->body, $6); + std::swap(tree->constants, $2); + std::swap(tree->types , $3); + std::swap(tree->variables, $4); + std::swap(tree->procedures, $5); + std::swap(tree->body, $7); driver.tree.reset(tree); } @@ -211,12 +211,12 @@ procedure_heading: } } procedure_definition: - "proc" identifier_definition procedure_heading ";" block + "proc" identifier_definition procedure_heading block { - $$ = new boot::procedure_definition(boot::make_position(@1), std::move($2), $3.second, $5); + $$ = new boot::procedure_definition(boot::make_position(@1), std::move($2), $3.second, $4); std::swap($3.first, $$->parameter_names); } - | "proc" identifier_definition procedure_heading ";" "extern" + | "proc" identifier_definition procedure_heading "extern" { $$ = new boot::procedure_definition(boot::make_position(@1), std::move($2), $3.second); std::swap($3.first, $$->parameter_names); @@ -419,8 +419,6 @@ statement: | return_statement { $$ = $1; } | "break" IDENTIFIER { $$ = new boot::escape_statement(boot::make_position(@1), boot::escape_direction::end, $2); } - | "repeat" IDENTIFIER - { $$ = new boot::escape_statement(boot::make_position(@1), boot::escape_direction::begin, $2); } | call_expression { $$ = $1; } | "defer" statements "end" { $$ = new boot::defer_statement(boot::make_position(@1), std::move($2)); } | "case" expression "of" switch_cases else_statements "end" diff --git a/source.elna b/source.elna index e175cee..27c74ed 100644 --- a/source.elna +++ b/source.elna @@ -1,7 +1,12 @@ +program + const SEEK_SET* := 0 SEEK_CUR* := 1 SEEK_END* := 2 + STDIN := 0 + STDOUT := 1 + STDERR := 2 type TokenKind* = ( @@ -19,12 +24,12 @@ type _extern, _const, _var, - array, + _case, _of, _type, _record, _union, - pointer, + pipe, to, boolean, _nil, @@ -64,7 +69,9 @@ type string, _defer, exclamation, - arrow + arrow, + trait, + _program ) Position* = record line: Word; @@ -106,56 +113,58 @@ type end CommandLine* = record input: ^Char; - tokenize: Bool; - syntax_tree: Bool + lex: Bool; + parse: Bool end (* External procedures. *) -proc fopen(pathname: ^Char, mode: ^Char) -> ^FILE; extern -proc fclose(stream: ^FILE) -> Int; extern -proc fseek(stream: ^FILE, off: Int, whence: Int) -> Int; extern -proc rewind(stream: ^FILE); extern -proc ftell(stream: ^FILE) -> Int; extern -proc fread(ptr: ^Byte, size: Word, nmemb: Word, stream: ^FILE) -> Word; extern -proc write(fd: Int, buf: ^Byte, Word: Int) -> Int; extern -proc malloc(size: Word) -> ^Byte; extern -proc free(ptr: ^Byte); extern -proc calloc(nmemb: Word, size: Word) -> ^Byte; extern -proc realloc(ptr: ^Byte, size: Word) -> ^Byte; extern +proc fopen(pathname: ^Char, mode: ^Char) -> ^FILE extern +proc fclose(stream: ^FILE) -> Int extern +proc fseek(stream: ^FILE, off: Int, whence: Int) -> Int extern +proc rewind(stream: ^FILE) extern +proc ftell(stream: ^FILE) -> Int extern +proc fread(ptr: ^Byte, size: Word, nmemb: Word, stream: ^FILE) -> Word extern +proc write(fd: Int, buf: ^Byte, Word: Int) -> Int extern -proc memset(ptr: ^Char, c: Int, n: Int) -> ^Char; extern +proc malloc(size: Word) -> ^Byte extern +proc free(ptr: ^Byte) extern +proc calloc(nmemb: Word, size: Word) -> ^Byte extern +proc realloc(ptr: ^Byte, size: Word) -> ^Byte extern -proc strcmp(s1: ^Char, s2: ^Char) -> Int; extern -proc strncmp(s1: ^Char, s2: ^Char, n: Word) -> Int; extern -proc strncpy(dst: ^Char, src: ^Char, dsize: Word) -> ^Char; extern -proc strcpy(dst: ^Char, src: ^Char) -> ^Char; extern -proc strlen(ptr: ^Char) -> Word; extern +proc memset(ptr: ^Char, c: Int, n: Int) -> ^Char extern -proc perror(s: ^Char); extern -proc exit(code: Int) -> !; extern +proc strcmp(s1: ^Char, s2: ^Char) -> Int extern +proc strncmp(s1: ^Char, s2: ^Char, n: Word) -> Int extern +proc strncpy(dst: ^Char, src: ^Char, dsize: Word) -> ^Char extern +proc strcpy(dst: ^Char, src: ^Char) -> ^Char extern +proc strlen(ptr: ^Char) -> Word extern + +proc perror(s: ^Char) extern +proc exit(code: Int) -> ! extern (* Standard procedures. *) -proc reallocarray(ptr: ^Byte, n: Word, size: Word) -> ^Byte; + +proc reallocarray(ptr: ^Byte, n: Word, size: Word) -> ^Byte begin return realloc(ptr, n * size) end -proc write_s(value: String); +proc write_s(value: String) begin write(0, cast(value.ptr: ^Byte), cast(value.length: Int)) end -proc write_z(value: ^Char); +proc write_z(value: ^Char) begin write(0, cast(value: ^Byte), cast(strlen(value): Int)) end -proc write_b(value: Bool); +proc write_b(value: Bool) begin if value then write_s("true") @@ -164,12 +173,12 @@ begin end end -proc write_c(value: Char); +proc write_c(value: Char) begin write(0, cast(@value: ^Byte), 1) end -proc write_i(value: Int); +proc write_i(value: Int) var digit: Int n: Word @@ -193,42 +202,42 @@ begin end end -proc write_u(value: Word); +proc write_u(value: Word) begin write_i(cast(value: Int)) end -proc is_digit(c: Char) -> Bool; +proc is_digit(c: Char) -> Bool begin return cast(c: Int) >= cast('0': Int) & cast(c: Int) <= cast('9': Int) end -proc is_alpha(c: Char) -> Bool; +proc is_alpha(c: Char) -> Bool begin return cast(c: Int) >= cast('A': Int) & cast(c: Int) <= cast('z': Int) end -proc is_alnum(c: Char) -> Bool; +proc is_alnum(c: Char) -> Bool begin return is_digit(c) or is_alpha(c) end -proc is_space(c: Char) -> Bool; +proc is_space(c: Char) -> Bool begin return c = ' ' or c = '\n' or c = '\t' end -proc substring(string: String, start: Word, count: Word) -> String; +proc substring(string: String, start: Word, count: Word) -> String begin return String(string.ptr + start, count) end -proc open_substring(string: String, start: Word) -> String; +proc open_substring(string: String, start: Word) -> String begin return substring(string, start, string.length - start) end -proc string_dup(origin: String) -> String; +proc string_dup(origin: String) -> String var copy: ^Char begin @@ -238,7 +247,7 @@ begin return String(copy, origin.length) end -proc string_buffer_new() -> StringBuffer; +proc string_buffer_new() -> StringBuffer var result: StringBuffer begin @@ -249,7 +258,7 @@ begin return result end -proc string_buffer_push(buffer: ^StringBuffer, char: Char); +proc string_buffer_push(buffer: ^StringBuffer, char: Char) begin if buffer^.size >= buffer^.capacity then buffer^.capacity := buffer^.capacity + 1024u; @@ -259,12 +268,12 @@ begin buffer^.size := buffer^.size + 1u end -proc string_buffer_pop(buffer: ^StringBuffer, count: Word); +proc string_buffer_pop(buffer: ^StringBuffer, count: Word) begin buffer^.size := buffer^.size - count end -proc string_buffer_clear(buffer: ^StringBuffer) -> String; +proc string_buffer_clear(buffer: ^StringBuffer) -> String var result: String begin @@ -274,15 +283,10 @@ begin end (* - End of standard procedures. + Source code stream procedures. *) -proc make_position() -> Position; -begin - return Position(1u, 1u) -end - -proc read_source(filename: ^Char) -> ^SourceFile; +proc read_source(filename: ^Char) -> ^SourceFile var result: ^SourceFile file_handle: ^FILE @@ -298,7 +302,70 @@ begin return result end -proc escape_char(escape: Char, result: ^Char) -> Bool; +proc source_file_empty(source_input: ^Byte) -> Bool +var + source_file: ^SourceFile +begin + source_file := cast(source_input: ^SourceFile); + + if source_file^.index > source_file^.size then + source_file^.size := fread(cast(@source_file^.buffer: ^Byte), 1u, 1024u, source_file^.handle); + source_file^.index := 1u + end; + + return source_file^.size = 0u +end + +proc source_file_head(source_input: ^Byte) -> Char +var + source_file: ^SourceFile +begin + source_file := cast(source_input: ^SourceFile); + + return source_file^.buffer[source_file^.index] +end + +proc source_file_advance(source_input: ^Byte) +var + source_file: ^SourceFile +begin + source_file := cast(source_input: ^SourceFile); + + source_file^.index := source_file^.index + 1u +end + +proc source_code_empty(source_code: ^SourceCode) -> Bool +begin + return source_code^.empty(source_code^.input) +end + +proc source_code_head(source_code: SourceCode) -> Char +begin + return source_code.head(source_code.input) +end + +proc source_code_advance(source_code: ^SourceCode) +begin + source_code^.advance(source_code^.input); + source_code^.position.column := source_code^.position.column +end + +proc source_code_break(source_code: ^SourceCode) +begin + source_code^.position.line := source_code^.position.line + 1u; + source_code^.position.column := 0u +end + +proc source_code_expect(source_code: ^SourceCode, expected: Char) -> Bool +begin + return ~source_code_empty(source_code) & source_code_head(source_code^) = expected +end + +(* + Token procedures. +*) + +proc escape_char(escape: Char, result: ^Char) -> Bool var successful: Bool begin @@ -344,66 +411,7 @@ begin return successful end -proc source_file_empty(source_input: ^Byte) -> Bool; -var - source_file: ^SourceFile -begin - source_file := cast(source_input: ^SourceFile); - - if source_file^.index > source_file^.size then - source_file^.size := fread(cast(@source_file^.buffer: ^Byte), 1u, 1024u, source_file^.handle); - source_file^.index := 1u - end; - - return source_file^.size = 0u -end - -proc source_file_head(source_input: ^Byte) -> Char; -var - source_file: ^SourceFile -begin - source_file := cast(source_input: ^SourceFile); - - return source_file^.buffer[source_file^.index] -end - -proc source_file_advance(source_input: ^Byte); -var - source_file: ^SourceFile -begin - source_file := cast(source_input: ^SourceFile); - - source_file^.index := source_file^.index + 1u -end - -proc source_code_empty(source_code: ^SourceCode) -> Bool; -begin - return source_code^.empty(source_code^.input) -end - -proc source_code_head(source_code: SourceCode) -> Char; -begin - return source_code.head(source_code.input) -end - -proc source_code_advance(source_code: ^SourceCode); -begin - source_code^.advance(source_code^.input); - source_code^.position.column := source_code^.position.column -end - -proc source_code_break(source_code: ^SourceCode); -begin - source_code^.position.line := source_code^.position.line + 1u; - source_code^.position.column := 0u -end - -proc source_code_expect(source_code: ^SourceCode, expected: Char) -> Bool; -begin - return ~source_code_empty(source_code) & source_code_head(source_code^) = expected -end - -proc skip_spaces(source_code: ^SourceCode); +proc skip_spaces(source_code: ^SourceCode) var current: Char begin @@ -419,12 +427,12 @@ begin end end -proc is_ident(char: Char) -> Bool; +proc is_ident(char: Char) -> Bool begin return is_alnum(char) or char = '_' end -proc lex_identifier(source_code: ^SourceCode, token_content: ^StringBuffer); +proc lex_identifier(source_code: ^SourceCode, token_content: ^StringBuffer) var content_length: Word begin @@ -434,7 +442,7 @@ begin end end -proc lex_comment(source_code: ^SourceCode, token_content: ^StringBuffer) -> Bool; +proc lex_comment(source_code: ^SourceCode, token_content: ^StringBuffer) -> Bool var trailing: Word begin @@ -457,7 +465,7 @@ begin return trailing = 2u end -proc lex_character(source_code: ^SourceCode, token_content: ^Char) -> Bool; +proc lex_character(source_code: ^SourceCode, token_content: ^Char) -> Bool var successful: Bool begin @@ -479,7 +487,7 @@ begin return successful end -proc lex_string(source_code: ^SourceCode, token_content: ^StringBuffer) -> Bool; +proc lex_string(source_code: ^SourceCode, token_content: ^StringBuffer) -> Bool var token_end, constructed_string: ^Char token_length: Word @@ -504,7 +512,7 @@ begin return is_valid end -proc lex_number(source_code: ^SourceCode, token_content: ^Int); +proc lex_number(source_code: ^SourceCode, token_content: ^Int) begin token_content^ := 0; @@ -515,7 +523,7 @@ begin end end -proc print_tokens(tokens: ^Token, tokens_size: Word); +proc print_tokens(tokens: ^Token, tokens_size: Word) var current_token: ^Token i: Word @@ -549,8 +557,8 @@ begin write_s("CONST") | TokenKind._var: write_s("VAR") - | TokenKind.array: - write_s("ARRAY") + | TokenKind._case: + write_s("CASE") | TokenKind._of: write_s("OF") | TokenKind._type: @@ -559,8 +567,8 @@ begin write_s("RECORD") | TokenKind._union: write_s("UNION") - | TokenKind.pointer: - write_s("POINTER") + | TokenKind.pipe: + write_s("|") | TokenKind.to: write_s("TO") | TokenKind.boolean: @@ -570,11 +578,11 @@ begin | TokenKind._nil: write_s("NIL") | TokenKind.and: - write_s("AND") + write_s("&") | TokenKind._or: write_s("OR") | TokenKind.not: - write_s("NOT") + write_s("~") | TokenKind._return: write_s("RETURN") | TokenKind._cast: @@ -587,6 +595,9 @@ begin write_c('<'); write_s(current_token^.value.string); write_c('>') + | TokenKind.trait: + write_c('#'); + write_s(current_token^.value.string) | TokenKind.left_paren: write_s("(") | TokenKind.right_paren: @@ -653,6 +664,8 @@ begin write_c('!') | TokenKind.arrow: write_s("->") + | TokenKind._program: + write_s("PROGRAM") else write_s("UNKNOWN<"); write_i(cast(current_token^.kind: Int)); @@ -665,68 +678,62 @@ begin write_c('\n') end -proc categorize_identifier(token_content: String) -> Token; +proc categorize_identifier(token_content: String) -> Token var current_token: Token begin - if "if" = token_content then + if token_content = "if" then current_token.kind := TokenKind._if - elsif "then" = token_content then + elsif token_content = "then" then current_token.kind := TokenKind._then - elsif "else" = token_content then + elsif token_content = "else" then current_token.kind := TokenKind._else - elsif "elsif" = token_content then + elsif token_content = "elsif" then current_token.kind := TokenKind._elsif - elsif "while" = token_content then + elsif token_content = "while" then current_token.kind := TokenKind._while - elsif "do" = token_content then + elsif token_content = "do" then current_token.kind := TokenKind._do - elsif "proc" = token_content then + elsif token_content = "proc" then current_token.kind := TokenKind._proc - elsif "begin" = token_content then + elsif token_content = "begin" then current_token.kind := TokenKind._begin - elsif "end" = token_content then + elsif token_content = "end" then current_token.kind := TokenKind._end - elsif "extern" = token_content then + elsif token_content = "extern" then current_token.kind := TokenKind._extern - elsif "const" = token_content then + elsif token_content = "const" then current_token.kind := TokenKind._const - elsif "var" = token_content then + elsif token_content = "var" then current_token.kind := TokenKind._var - elsif "array" = token_content then - current_token.kind := TokenKind.array - elsif "of" = token_content then + elsif token_content = "case" then + current_token.kind := TokenKind._case + elsif token_content = "of" then current_token.kind := TokenKind._of - elsif "type" = token_content then + elsif token_content = "type" then current_token.kind := TokenKind._type - elsif "record" = token_content then + elsif token_content = "record" then current_token.kind := TokenKind._record - elsif "union" = token_content then + elsif token_content = "union" then current_token.kind := TokenKind._union - elsif "pointer" = token_content then - current_token.kind := TokenKind.pointer - elsif "to" = token_content then - current_token.kind := TokenKind.to - elsif "true" = token_content then + elsif token_content = "true" then current_token.kind := TokenKind.boolean; current_token.value.boolean_value := true - elsif "false" = token_content then + elsif token_content = "false" then current_token.kind := TokenKind.boolean; current_token.value.boolean_value := false - elsif "nil" = token_content then + elsif token_content = "nil" then current_token.kind := TokenKind._nil - elsif "and" = token_content then - current_token.kind := TokenKind.and - elsif "or" = token_content then + elsif token_content = "or" then current_token.kind := TokenKind._or - elsif "not" = token_content then - current_token.kind := TokenKind.not - elsif "return" = token_content then + elsif token_content = "return" then current_token.kind := TokenKind._return - elsif "cast" = token_content then + elsif token_content = "cast" then current_token.kind := TokenKind._cast - elsif "defer" = token_content then + elsif token_content = "defer" then current_token.kind := TokenKind._defer + elsif token_content = "program" then + current_token.kind := TokenKind._program else current_token.kind := TokenKind.identifier; current_token.value.string := string_dup(token_content) @@ -735,7 +742,7 @@ begin return current_token end -proc tokenize(source_code: SourceCode, tokens_size: ^Word) -> ^Token; +proc tokenize(source_code: SourceCode, tokens_size: ^Word) -> ^Token var tokens, current_token: ^Token first_char: Char @@ -755,6 +762,12 @@ begin if is_alpha(first_char) or first_char = '_' then lex_identifier(@source_code, @token_buffer); current_token^ := categorize_identifier(string_buffer_clear(@token_buffer)) + elsif first_char = '#' then + source_code_advance(@source_code); + lex_identifier(@source_code, @token_buffer); + + current_token^.kind := TokenKind.trait; + current_token^.value.string := string_dup(string_buffer_clear(@token_buffer)) elsif is_digit(first_char) then lex_number(@source_code, @current_token^.value.int_value); @@ -894,6 +907,15 @@ begin elsif first_char = '!' then current_token^.kind := TokenKind.exclamation; source_code_advance(@source_code) + elsif first_char = '&' then + current_token^.kind := TokenKind.and; + source_code_advance(@source_code) + elsif first_char = '~' then + current_token^.kind := TokenKind.not; + source_code_advance(@source_code) + elsif first_char = '|' then + current_token^.kind := TokenKind.pipe; + source_code_advance(@source_code) else current_token^.kind := TokenKind.unknown; source_code_advance(@source_code) @@ -912,7 +934,11 @@ begin return tokens end -proc parse_command_line*(argc: Int, argv: ^^Char) -> ^CommandLine; +(* + Command line handling. +*) + +proc parse_command_line*(argc: Int, argv: ^^Char) -> ^CommandLine var parameter: ^^Char i: Int @@ -920,23 +946,26 @@ var begin i := 1; result := cast(malloc(#size(CommandLine)): ^CommandLine); - result^.tokenize := false; - result^.syntax_tree := false; + result^.lex := false; + result^.parse := false; result^.input := nil; while i < argc do parameter := argv + i; - if strcmp(parameter^, "--tokenize\0".ptr) = 0 then - result^.tokenize := true - elsif strcmp(parameter^, "--syntax-tree\0".ptr) = 0 then - result^.syntax_tree := true + if strcmp(parameter^, "--lex\0".ptr) = 0 then + result^.lex := true + elsif strcmp(parameter^, "--parse\0".ptr) = 0 then + result^.parse := true elsif parameter^^ <> '-' then + if result^.input <> nil then + write_s("Fatal error: Only one source file can be given.\n"); + return nil + end; result^.input := parameter^ else - write_s("Fatal error: Unknown command line options:"); + write_s("Fatal error: Unknown command line options: "); - write_c(' '); write_z(parameter^); write_s(".\n"); @@ -953,7 +982,11 @@ begin return result end -proc process(argc: Int, argv: ^^Char) -> Int; +(* + Compilation entry. +*) + +proc process(argc: Int, argv: ^^Char) -> Int var tokens: ^Token tokens_size: Word @@ -969,7 +1002,7 @@ begin end; if return_code = 0 then - source_code.position := make_position(); + source_code.position := Position(1u, 1u); source_code.input := cast(read_source(command_line^.input): ^Byte); source_code.empty := source_file_empty; @@ -986,7 +1019,7 @@ begin fclose(cast(source_code.input: ^SourceFile)^.handle); - if command_line^.tokenize then + if command_line^.lex then print_tokens(tokens, tokens_size) end end; diff --git a/tools/init.c b/tools/init.c deleted file mode 100644 index f463bcd..0000000 --- a/tools/init.c +++ /dev/null @@ -1,204 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#define FILENAME_BUFFER_SIZE 256 - -size_t read_command(int descriptor, char *command_buffer) -{ - ssize_t bytes_read = 0; - size_t read_so_far = 0; - - while ((bytes_read = read(descriptor, command_buffer + read_so_far, FILENAME_BUFFER_SIZE - read_so_far - 1)) > 0) - { - read_so_far += bytes_read; - if (read_so_far >= FILENAME_BUFFER_SIZE - 1) - { - break; - } - } - command_buffer[read_so_far] = 0; - return read_so_far; -} - -enum status -{ - status_success, - status_failure, - status_warning, - status_fatal -}; - -unsigned int make_path(char *destination, const char *directory, const char *filename, const char *extension) -{ - unsigned int i = 0; - - for (; i < FILENAME_BUFFER_SIZE; i++) - { - if (directory[i] == 0) - { - break; - } - destination[i] = directory[i]; - } - for (int j = 0; i < FILENAME_BUFFER_SIZE; i++, j++) - { - if (filename[j] == 0) - { - break; - } - destination[i] = filename[j]; - } - if (extension == NULL) - { - goto done; - } - for (int j = 0; i < FILENAME_BUFFER_SIZE; i++, j++) - { - if (extension[j] == 0) - { - break; - } - destination[i] = extension[j]; - } -done: - destination[i] = 0; - - return i; -} - -enum status run_test(const char *file_entry_name) -{ - printf("Running %s. ", file_entry_name); - - char filename[FILENAME_BUFFER_SIZE]; - char command_buffer[FILENAME_BUFFER_SIZE]; - char file_buffer[256]; - int pipe_ends[2]; - - if (pipe(pipe_ends) == -1) - { - perror("pipe"); - return status_fatal; - } - make_path(filename, "./tests/", file_entry_name, NULL); - - int child_pid = fork(); - if (child_pid == -1) - { - return status_fatal; - } - else if (child_pid == 0) - { - close(STDIN_FILENO); - close(STDERR_FILENO); - close(pipe_ends[0]); // Close the read end. - - if (dup2(pipe_ends[1], STDOUT_FILENO) == -1) - { - perror("dup2"); - } - else - { - execl(filename, filename); - perror("execl"); - } - close(STDOUT_FILENO); - close(pipe_ends[1]); - _exit(1); - } - else - { - close(pipe_ends[1]); // Close the write end. - read_command(pipe_ends[0], command_buffer); - close(pipe_ends[0]); - - int wait_status = 0; - - make_path(filename, "./expectations/", file_entry_name, ".txt"); - - FILE *expectation_descriptor = fopen(filename, "r"); - - if (expectation_descriptor == NULL) - { - return status_warning; - } - size_t read_from_file = fread(file_buffer, 1, sizeof(file_buffer) - 1, expectation_descriptor); - fclose(expectation_descriptor); - - file_buffer[read_from_file] = 0; - for (unsigned int i = 0; ; ++i) - { - if (command_buffer[i] == 0 && file_buffer[i] == 0) - { - fwrite("\n", 1, 1, stdout); - return status_success; - } - else if (command_buffer[i] != file_buffer[i]) - { - printf("Failed. Got:\n%s", command_buffer); - return status_failure; - } - } - } -} - -struct summary -{ - size_t total; - size_t failure; - size_t success; -}; - -void walk() -{ - DIR *directory_stream = opendir("./tests"); - struct dirent *file_entry; - - struct summary test_summary = { .total = 0, .failure = 0, .success = 0 }; - - while ((file_entry = readdir(directory_stream)) != NULL) - { - if (file_entry->d_name[0] == '.') - { - continue; - } - ++test_summary.total; - switch (run_test(file_entry->d_name)) - { - case status_failure: - ++test_summary.failure; - break; - case status_success: - ++test_summary.success; - break; - case status_warning: - break; - case status_fatal: - goto end_walk; - } - } - printf("Successful: %lu, Failed: %lu, Total: %lu.\n", - test_summary.success, test_summary.failure, test_summary.total); -end_walk: - closedir(directory_stream); -} - -int main() -{ - int dev_console = open("/dev/console", O_WRONLY); - if (dev_console != -1) - { - dup2(dev_console, STDOUT_FILENO); - walk(); - close(dev_console); - } - sync(); - reboot(RB_POWER_OFF); - - return 1; -}