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 +++++++++++ .../java/com/seaofnodes/simple/BoundaryTest.java | 12 +- .../java/com/seaofnodes/simple/BrainFuckTest.java | 2 +- .../java/com/seaofnodes/simple/Chapter04Test.java | 4 +- .../java/com/seaofnodes/simple/Chapter05Test.java | 6 +- .../java/com/seaofnodes/simple/Chapter07Test.java | 18 +- .../java/com/seaofnodes/simple/Chapter08Test.java | 6 +- .../java/com/seaofnodes/simple/Chapter09Test.java | 4 +- .../java/com/seaofnodes/simple/Chapter10Test.java | 9 +- .../java/com/seaofnodes/simple/Chapter12Test.java | 2 +- .../java/com/seaofnodes/simple/Chapter13Test.java | 8 +- .../com/seaofnodes/simple/Chapter14FloatTest.java | 6 +- .../java/com/seaofnodes/simple/Chapter15Test.java | 4 +- .../java/com/seaofnodes/simple/Chapter16Test.java | 4 +- .../java/com/seaofnodes/simple/Chapter17bTest.java | 23 +-- .../java/com/seaofnodes/simple/Chapter18Test.java | 46 ++--- .../java/com/seaofnodes/simple/Chapter19Test.java | 36 ++-- .../java/com/seaofnodes/simple/Chapter20Test.java | 14 +- .../java/com/seaofnodes/simple/Chapter21Test.java | 27 +-- .../java/com/seaofnodes/simple/Chapter22Test.java | 11 +- .../java/com/seaofnodes/simple/Chapter23Test.java | 2 +- .../java/com/seaofnodes/simple/Chapter24Test.java | 7 +- .../java/com/seaofnodes/simple/Chapter25Test.java | 26 +-- .../simple/ClassInitDependenciesTest.java | 14 +- .../seaofnodes/simple/CompoundAssignmentTest.java | 10 +- .../seaofnodes/simple/ConstructionGroupTest.java | 8 +- .../java/com/seaofnodes/simple/EscapeFlowTest.java | 10 +- .../seaofnodes/simple/FinalConstructionTest.java | 10 +- .../seaofnodes/simple/IncrementalBuildTest.java | 6 +- .../test/java/com/seaofnodes/simple/Int32Test.java | 94 ++++++++++ .../java/com/seaofnodes/simple/MergeSortTest.java | 13 +- .../java/com/seaofnodes/simple/MinusNodeTest.java | 2 +- .../seaofnodes/simple/ParserDiagnosticTest.java | 191 +++++++++++++++++++++ .../com/seaofnodes/simple/PartialReadTest.java | 6 +- .../com/seaofnodes/simple/RuntimeArgsTest.java | 29 ++++ .../seaofnodes/simple/SelectiveLoadingTest.java | 32 ++-- .../seaofnodes/simple/SeparateCompilationTest.java | 8 +- .../test/java/com/seaofnodes/simple/Simple.java | 30 +++- .../java/com/seaofnodes/simple/StreamIOTest.java | 138 +++++++++++++++ .../com/seaofnodes/simple/StringFormatTest.java | 158 +++++++++++++++++ .../com/seaofnodes/simple/TerminalInputTest.java | 184 ++++++++++++++++++++ .../src/test/java/com/seaofnodes/simple/TestC.java | 53 +++++- .../java/com/seaofnodes/simple/TicTacToeTest.java | 92 ++++++++++ .../simple/codegen/RegAllocTestSupport.java | 70 +++++++- .../java/com/seaofnodes/simple/progs/extern_data.c | 7 + .../test/java/com/seaofnodes/simple/progs/int32.c | 26 +++ .../com/seaofnodes/simple/progs/runtime_args.c | 35 ++++ .../test/java/com/seaofnodes/simple/progs/sort.c | 4 +- .../java/com/seaofnodes/simple/progs/stream_io.c | 49 ++++++ .../java/com/seaofnodes/simple/progs/stringHash.c | 2 +- chapter30/src/test/smp/examples/A_helloWorld.smp | 2 - chapter30/src/test/smp/examples/BubbleSort.smp | 36 ---- chapter30/src/test/smp/examples/Capitalize.smp | 22 --- chapter30/src/test/smp/examples/Dijkstra.smp | 72 -------- chapter30/src/test/smp/examples/FileIO.smp | 50 ------ chapter30/src/test/smp/separate/client/Main.smp | 2 +- 99 files changed, 2188 insertions(+), 647 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 create mode 100644 chapter30/src/test/java/com/seaofnodes/simple/Int32Test.java create mode 100644 chapter30/src/test/java/com/seaofnodes/simple/ParserDiagnosticTest.java create mode 100644 chapter30/src/test/java/com/seaofnodes/simple/RuntimeArgsTest.java create mode 100644 chapter30/src/test/java/com/seaofnodes/simple/StreamIOTest.java create mode 100644 chapter30/src/test/java/com/seaofnodes/simple/StringFormatTest.java create mode 100644 chapter30/src/test/java/com/seaofnodes/simple/TerminalInputTest.java create mode 100644 chapter30/src/test/java/com/seaofnodes/simple/TicTacToeTest.java create mode 100644 chapter30/src/test/java/com/seaofnodes/simple/progs/int32.c create mode 100644 chapter30/src/test/java/com/seaofnodes/simple/progs/runtime_args.c create mode 100644 chapter30/src/test/java/com/seaofnodes/simple/progs/stream_io.c delete mode 100644 chapter30/src/test/smp/examples/A_helloWorld.smp delete mode 100644 chapter30/src/test/smp/examples/BubbleSort.smp delete mode 100644 chapter30/src/test/smp/examples/Capitalize.smp delete mode 100644 chapter30/src/test/smp/examples/Dijkstra.smp delete mode 100644 chapter30/src/test/smp/examples/FileIO.smp (limited to 'chapter30/src') 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