From 06fb5aceeb65a75b1cd643debd667c52fdcafcb9 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Sun, 8 Nov 2026 14:10:02 -0800 Subject: Better error messages Better error messages. Buffs to the std lib; getch() to fetch a single character; streaming I/O handles retries, and large sizes, with better error reporting. Formatted strings. Default int moved to i32. TicTacToe, the game. --- chapter30/src/main/c/simple_crt.c | 124 ++++++++++++------ .../com/seaofnodes/simple/codegen/BuildLRG.java | 2 +- .../com/seaofnodes/simple/codegen/Coalesce.java | 2 +- .../java/com/seaofnodes/simple/codegen/IFG.java | 8 +- .../com/seaofnodes/simple/codegen/InstSelect.java | 8 +- .../com/seaofnodes/simple/codegen/Machine.java | 12 ++ .../com/seaofnodes/simple/codegen/ParseAll.java | 21 ++-- .../com/seaofnodes/simple/codegen/RegAlloc.java | 51 ++++++-- .../com/seaofnodes/simple/codegen/SourceError.java | 16 ++- .../java/com/seaofnodes/simple/node/CallNode.java | 2 +- .../com/seaofnodes/simple/node/FldOffNode.java | 2 +- .../com/seaofnodes/simple/node/FormatNode.java | 33 +++++ .../java/com/seaofnodes/simple/node/LoadNode.java | 3 + .../java/com/seaofnodes/simple/node/ScopeNode.java | 1 + .../seaofnodes/simple/node/cpus/arm/ExtendARM.java | 15 ++- .../com/seaofnodes/simple/node/cpus/arm/arm.java | 3 +- .../simple/node/cpus/riscv/ExtendRISC.java | 15 ++- .../seaofnodes/simple/node/cpus/riscv/riscv.java | 3 +- .../simple/node/cpus/x86_64_v2/ExtendX86.java | 15 ++- .../simple/node/cpus/x86_64_v2/x86_64_v2.java | 4 +- .../com/seaofnodes/simple/parser/Allocations.java | 20 +-- .../com/seaofnodes/simple/parser/ControlFlow.java | 4 +- .../com/seaofnodes/simple/parser/Declarations.java | 21 ++-- .../com/seaofnodes/simple/parser/Expressions.java | 41 ++++-- .../com/seaofnodes/simple/parser/Functions.java | 13 +- .../java/com/seaofnodes/simple/parser/Lexer.java | 139 ++++++++++++++++----- .../java/com/seaofnodes/simple/parser/Parser.java | 23 +++- .../com/seaofnodes/simple/parser/References.java | 46 +++++-- .../com/seaofnodes/simple/parser/Statements.java | 27 ++-- .../java/com/seaofnodes/simple/parser/Strings.java | 59 +++++++++ .../java/com/seaofnodes/simple/parser/Structs.java | 6 +- .../java/com/seaofnodes/simple/parser/Types.java | 6 +- .../com/seaofnodes/simple/type/TypeConAry.java | 15 +++ .../com/seaofnodes/simple/type/TypeConAryB.java | 2 +- .../com/seaofnodes/simple/type/TypeInteger.java | 10 +- chapter30/src/main/smp/sys.smp | 2 + chapter30/src/main/smp/sys/ary.smp | 26 +++- chapter30/src/main/smp/sys/aryi64.smp | 25 ++-- chapter30/src/main/smp/sys/aryu8.smp | 45 +++---- chapter30/src/main/smp/sys/io.smp | 75 +++++++---- chapter30/src/main/smp/sys/libc.smp | 25 +++- chapter30/src/main/smp/sys/rt.smp | 3 + chapter30/src/main/smp/sys/rt/simple_args.smp | 22 ++++ chapter30/src/main/smp/sys/text.smp | 98 +++++++++++++++ 44 files changed, 838 insertions(+), 255 deletions(-) create mode 100644 chapter30/src/main/java/com/seaofnodes/simple/node/FormatNode.java create mode 100644 chapter30/src/main/java/com/seaofnodes/simple/parser/Strings.java create mode 100644 chapter30/src/main/smp/sys/rt.smp create mode 100644 chapter30/src/main/smp/sys/rt/simple_args.smp create mode 100644 chapter30/src/main/smp/sys/text.smp (limited to 'chapter30/src/main') diff --git a/chapter30/src/main/c/simple_crt.c b/chapter30/src/main/c/simple_crt.c index 8656a3cc..438ae5f9 100644 --- a/chapter30/src/main/c/simple_crt.c +++ b/chapter30/src/main/c/simple_crt.c @@ -1,58 +1,106 @@ #include +#include #include #include #include +#include +#include // errno may be thread-local or a macro calling a platform accessor. int32_t simple_errno(void) { return errno; } +int32_t simple_set_errno(int32_t error) { errno = error; return -1; } +// Export platform errno constants; the buffering and retry loops are Simple. +// C identifiers avoid the macros; linker names match the Simple bindings. +int32_t simple_EINTR __asm__("EINTR") = EINTR; +int32_t simple_EIO __asm__("EIO") = EIO; +int32_t simple_EINVAL __asm__("EINVAL") = EINVAL; +int32_t simple_EOVERFLOW __asm__("EOVERFLOW") = EOVERFLOW; -// Native process arguments converted to ordinary Simple array layouts. -// The outer array is mutable; every contained byte array is published as -// immutable and non-null to Simple code. +// Read one stdin byte, without terminal line editing or echo. POSIX terminals +// include Cygwin consoles; redirected files/pipes need no mode change. +// Return 0..255, or -1 with errno set to EOF or an OS error. Control characters, +// including Ctrl-C, are returned as bytes so the mode is restored first. +int32_t simple_getch(void) { + struct termios saved, mode; + int terminal = tcgetattr(STDIN_FILENO, &saved) == 0; + if (!terminal && errno != ENOTTY) + return -1; + if (terminal) { + mode = saved; + mode.c_lflag &= ~(ICANON | ECHO | ECHONL | ISIG | IEXTEN); + mode.c_iflag &= ~IXON; + mode.c_cc[VMIN] = 1; + mode.c_cc[VTIME] = 0; + if (tcsetattr(STDIN_FILENO, TCSANOW, &mode) != 0) + return -1; + } + unsigned char byte; + ssize_t count = read(STDIN_FILENO, &byte, 1); + int read_errno = errno; + if (terminal) { + int restored; + do { + restored = tcsetattr(STDIN_FILENO, TCSANOW, &saved); + } while (restored != 0 && errno == EINTR); + if (restored != 0) + return -1; + } + if (count < 0) { + errno = read_errno; + return -1; + } + if (count == 0) { + errno = EOF; + return -1; + } + return byte; +} + +// Simple byte-array layout, used only by the floating-point formatter. typedef struct SimpleBytes { uint32_t len; unsigned char bytes[]; } SimpleBytes; -typedef struct SimpleArgs { - uint32_t len; - uint32_t pad; - SimpleBytes *args[]; -} SimpleArgs; +// Binary floating-point conversion is the only native formatting primitive. +// Buffering, integer conversion, string precision and padding live in sys.text. +SimpleBytes *simple_text_float(double value, int64_t spec, + int64_t width, int64_t precision) { + char kind = spec & 255; + if (!kind) { kind = 'g'; precision = 17; } + if (!strchr("fFeEgGaA", kind) || (spec & ~8191LL) || + width < 0 || width > INT32_MAX || precision < -1 || precision > INT32_MAX) + abort(); + char fmt[64], *p = fmt; + *p++ = '%'; + const char *flags = "-+ #0"; + for (int i=0; i<5; i++) if (spec & (256LL<= 0) p += sprintf(p, ".%d", (int)precision); + *p++ = kind; + *p = 0; + int len = snprintf(NULL, 0, fmt, value); + if (len < 0) abort(); + SimpleBytes *str = malloc(sizeof(*str) + (size_t)len + 1); + if (!str) abort(); + str->len = (uint32_t)len; + snprintf((char *)str->bytes, (size_t)len + 1, fmt, value); + return str; +} +// Simple does not yet dereference raw native pointers. Keep just the argv +// element access here; allocation and array construction live in sys/rt/simple_args.smp. +const char *simple_argv(char **argv, int64_t index) { + return argv[index]; +} __attribute__((CALL_CONV)) -extern int64_t simple_main(SimpleArgs *args); - -static SimpleArgs *simple_args(int argc, char **argv) { - if (argc < 0 || (uint64_t)argc > UINT32_MAX) - return NULL; - - SimpleArgs *args = calloc(1, sizeof(*args) + (size_t)argc * sizeof(args->args[0])); - if (args == NULL) - return NULL; - args->len = (uint32_t)argc; - - for (int i = 0; i < argc; i++) { - size_t len = strlen(argv[i]); - if (len > UINT32_MAX) { - free(args); - return NULL; - } - SimpleBytes *str = malloc(sizeof(*str) + len); - if (str == NULL) { - for (int j = 0; j < i; j++) free(args->args[j]); - free(args); - return NULL; - } - str->len = (uint32_t)len; - memcpy(str->bytes, argv[i], len); - args->args[i] = str; - } - return args; -} +extern void *simple_args(int64_t argc, char **argv); +__attribute__((CALL_CONV)) +extern int64_t simple_main(void *args); int main(int argc, char **argv) { - SimpleArgs *args = simple_args(argc, argv); + // simple_args fills every slot before publishing this as u8[~][]. + void *args = simple_args(argc, argv); if (args == NULL) return 1; // The language-level hidden receiver is not part of the emitted ABI for diff --git a/chapter30/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java b/chapter30/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java index 0b314978..8d0237ea 100644 --- a/chapter30/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java +++ b/chapter30/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java @@ -16,7 +16,7 @@ abstract public class BuildLRG { // true if no hard-conflicts, although we still might not color. public static boolean run(int round, RegAlloc alloc) { FunNode fun = null; - for( Node bb : alloc._code._cfg ) { + for( Node bb : alloc._cfg ) { if( bb instanceof FunNode fun2 ) fun = fun2; for( Node n : bb.outs() ) { diff --git a/chapter30/src/main/java/com/seaofnodes/simple/codegen/Coalesce.java b/chapter30/src/main/java/com/seaofnodes/simple/codegen/Coalesce.java index 689ceceb..dbde2f8b 100644 --- a/chapter30/src/main/java/com/seaofnodes/simple/codegen/Coalesce.java +++ b/chapter30/src/main/java/com/seaofnodes/simple/codegen/Coalesce.java @@ -13,7 +13,7 @@ abstract public class Coalesce { // Walk all the splits, looking for coalesce chances boolean progress = false; - for( CFGNode bb : alloc._code._cfg ) { + for( CFGNode bb : alloc._cfg ) { outer: for( int j=0; j