diff --git a/README.md b/README.md index 13c95ec..c036e35 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,9 @@ Notes: override the default `$HOME/.peppermint/store.json` location. - `version` prints `Peppermint Version 0.0.1[platform]`. - `bf` is a separate Brainfuck runner. With `--compile=true`, it also writes the - original Long-language-style output to `dist/test.long`. + original Long-language-style output to `dist/test.long`. The compiler follows + the upstream static conversion pass: it records cell values observed at `.` + instructions and ignores Brainfuck loop/control execution while compiling. ## Language behavior @@ -89,4 +91,5 @@ portable tab-separated project store at `$HOME/.peppermint/store.json` (or The test script rebuilds with strict C89 flags, then checks legacy direct-file execution, command dispatch, expressions, relational operators, mutable vs. constant assignment, builtins, project init/config/list/run behavior, version -output, and the Brainfuck runner/compiler command. +output, and the Brainfuck runner/compiler command including static Long output +semantics. diff --git a/peppermint.c b/peppermint.c index e786e28..f336fe3 100644 --- a/peppermint.c +++ b/peppermint.c @@ -1170,51 +1170,115 @@ static int pm_command_list(void) return 0; } +static int pm_bf_append_text(char **buffer, size_t *used, size_t *capacity, + const char *part) +{ + size_t length; + size_t needed; + size_t next_capacity; + char *next; + length = strlen(part); + needed = *used + length + 1U; + if (needed > *capacity) { + next_capacity = *capacity; + while (next_capacity < needed) { + if (next_capacity > ((size_t)-1) / 2U) return 0; + next_capacity *= 2U; + } + next = (char *)realloc(*buffer, next_capacity); + if (next == NULL) return 0; + *buffer = next; + *capacity = next_capacity; + } + memcpy(*buffer + *used, part, length + 1U); + *used += length; + return 1; +} + +static int pm_bf_ensure_compile_cell(int **cells, long index, long *capacity) +{ + long next_capacity; + int *next; + long i; + if (index < *capacity) return 1; + next_capacity = *capacity; + while (next_capacity <= index) { + if (next_capacity > 1073741823L) return 0; + next_capacity *= 2L; + } + next = (int *)realloc(*cells, (size_t)next_capacity * sizeof(int)); + if (next == NULL) return 0; + for (i = *capacity; i < next_capacity; i++) next[i] = 0; + *cells = next; + *capacity = next_capacity; + return 1; +} + static int pm_bf_compile(const char *source) { - int cells[PM_BF_CELLS]; - int pointer; + int *cells; + long capacity; + long pointer; const char *p; int value; - char compiled[PM_TEXT_MAX]; + char *compiled; char part[64]; char *dir; char *out; size_t used; - int i; - for (i = 0; i < PM_BF_CELLS; i++) cells[i] = 0; - pointer = 0; + size_t compiled_capacity; + int ok; + cells = (int *)calloc(1U, sizeof(int)); + compiled_capacity = 128U; + compiled = (char *)malloc(compiled_capacity); + if (cells == NULL || compiled == NULL) { + free(cells); + free(compiled); + return 0; + } + capacity = 1L; + pointer = 0L; value = 0; - compiled[0] = '\0'; used = 0U; + compiled[0] = '\0'; p = source; while (*p != '\0') { if (*p == '+') cells[pointer]++; else if (*p == '-') cells[pointer]--; - else if (*p == '>' && pointer < PM_BF_CELLS - 1) pointer++; - else if (*p == '<' && pointer > 0) pointer--; - else if (*p == '.') { + else if (*p == '>') { + pointer++; + if (!pm_bf_ensure_compile_cell(&cells, pointer, &capacity)) { + free(cells); + free(compiled); + return 0; + } + } else if (*p == '<') { + if (pointer != 0L) pointer--; + } else if (*p == '.') { int diff; diff = cells[pointer] - value; value = cells[pointer]; if (diff < 0) sprintf(part, "%d-", -diff); else sprintf(part, "%d+", diff); - if (used + strlen(part) < sizeof(compiled)) { - strcat(compiled, part); - used += strlen(part); + if (!pm_bf_append_text(&compiled, &used, &compiled_capacity, part)) { + free(cells); + free(compiled); + return 0; } } p++; } dir = pm_join_path(".", "dist"); - if (dir == NULL) return 0; + if (dir == NULL) { free(cells); free(compiled); return 0; } pm_make_dir(dir); out = pm_join_path(dir, "test.long"); free(dir); - if (out == NULL) return 0; - i = pm_write_file(out, compiled); + if (out == NULL) { free(cells); free(compiled); return 0; } + ok = pm_write_file(out, compiled); free(out); - return i; + free(cells); + free(compiled); + return ok; } static int pm_bf_execute(const char *source) diff --git a/test.sh b/test.sh index be6c095..995255c 100755 --- a/test.sh +++ b/test.sh @@ -96,6 +96,25 @@ if [ ! -f dist/test.long ]; then printf '%s\n' 'bf compile did not create dist/test.long' >&2 exit 1 fi +compiled=$(sed -n '1p' dist/test.long) +if [ "$compiled" != '9+' ]; then + printf '%s\n' "unexpected bf compile output:" "$compiled" >&2 + exit 1 +fi +rm -rf dist +: > /tmp/peppermint-c89-bf-far.bf +i=0 +while [ "$i" -lt 30000 ]; do + printf '>' >> /tmp/peppermint-c89-bf-far.bf + i=$((i + 1)) +done +printf '+.>+.<.' >> /tmp/peppermint-c89-bf-far.bf +./peppermint bf --file=/tmp/peppermint-c89-bf-far.bf --compile=true >/tmp/peppermint-c89-bf-far.out +compiled=$(sed -n '1p' dist/test.long) +if [ "$compiled" != '1+0+0+' ]; then + printf '%s\n' "bf compile collapsed cells beyond interpreter memory:" "$compiled" >&2 + exit 1 +fi rm -rf dist "$tmpdir" output=$(./peppermint version)