Match Brainfuck compiler output in C89 port
This commit is contained in:
@@ -44,7 +44,9 @@ Notes:
|
|||||||
override the default `$HOME/.peppermint/store.json` location.
|
override the default `$HOME/.peppermint/store.json` location.
|
||||||
- `version` prints `Peppermint Version 0.0.1[platform]`.
|
- `version` prints `Peppermint Version 0.0.1[platform]`.
|
||||||
- `bf` is a separate Brainfuck runner. With `--compile=true`, it also writes the
|
- `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
|
## 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
|
The test script rebuilds with strict C89 flags, then checks legacy direct-file
|
||||||
execution, command dispatch, expressions, relational operators, mutable vs.
|
execution, command dispatch, expressions, relational operators, mutable vs.
|
||||||
constant assignment, builtins, project init/config/list/run behavior, version
|
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.
|
||||||
|
|||||||
+81
-17
@@ -1170,51 +1170,115 @@ static int pm_command_list(void)
|
|||||||
return 0;
|
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)
|
static int pm_bf_compile(const char *source)
|
||||||
{
|
{
|
||||||
int cells[PM_BF_CELLS];
|
int *cells;
|
||||||
int pointer;
|
long capacity;
|
||||||
|
long pointer;
|
||||||
const char *p;
|
const char *p;
|
||||||
int value;
|
int value;
|
||||||
char compiled[PM_TEXT_MAX];
|
char *compiled;
|
||||||
char part[64];
|
char part[64];
|
||||||
char *dir;
|
char *dir;
|
||||||
char *out;
|
char *out;
|
||||||
size_t used;
|
size_t used;
|
||||||
int i;
|
size_t compiled_capacity;
|
||||||
for (i = 0; i < PM_BF_CELLS; i++) cells[i] = 0;
|
int ok;
|
||||||
pointer = 0;
|
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;
|
value = 0;
|
||||||
compiled[0] = '\0';
|
|
||||||
used = 0U;
|
used = 0U;
|
||||||
|
compiled[0] = '\0';
|
||||||
p = source;
|
p = source;
|
||||||
while (*p != '\0') {
|
while (*p != '\0') {
|
||||||
if (*p == '+') cells[pointer]++;
|
if (*p == '+') cells[pointer]++;
|
||||||
else if (*p == '-') cells[pointer]--;
|
else if (*p == '-') cells[pointer]--;
|
||||||
else if (*p == '>' && pointer < PM_BF_CELLS - 1) pointer++;
|
else if (*p == '>') {
|
||||||
else if (*p == '<' && pointer > 0) pointer--;
|
pointer++;
|
||||||
else if (*p == '.') {
|
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;
|
int diff;
|
||||||
diff = cells[pointer] - value;
|
diff = cells[pointer] - value;
|
||||||
value = cells[pointer];
|
value = cells[pointer];
|
||||||
if (diff < 0) sprintf(part, "%d-", -diff);
|
if (diff < 0) sprintf(part, "%d-", -diff);
|
||||||
else sprintf(part, "%d+", diff);
|
else sprintf(part, "%d+", diff);
|
||||||
if (used + strlen(part) < sizeof(compiled)) {
|
if (!pm_bf_append_text(&compiled, &used, &compiled_capacity, part)) {
|
||||||
strcat(compiled, part);
|
free(cells);
|
||||||
used += strlen(part);
|
free(compiled);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p++;
|
p++;
|
||||||
}
|
}
|
||||||
dir = pm_join_path(".", "dist");
|
dir = pm_join_path(".", "dist");
|
||||||
if (dir == NULL) return 0;
|
if (dir == NULL) { free(cells); free(compiled); return 0; }
|
||||||
pm_make_dir(dir);
|
pm_make_dir(dir);
|
||||||
out = pm_join_path(dir, "test.long");
|
out = pm_join_path(dir, "test.long");
|
||||||
free(dir);
|
free(dir);
|
||||||
if (out == NULL) return 0;
|
if (out == NULL) { free(cells); free(compiled); return 0; }
|
||||||
i = pm_write_file(out, compiled);
|
ok = pm_write_file(out, compiled);
|
||||||
free(out);
|
free(out);
|
||||||
return i;
|
free(cells);
|
||||||
|
free(compiled);
|
||||||
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pm_bf_execute(const char *source)
|
static int pm_bf_execute(const char *source)
|
||||||
|
|||||||
@@ -96,6 +96,25 @@ if [ ! -f dist/test.long ]; then
|
|||||||
printf '%s\n' 'bf compile did not create dist/test.long' >&2
|
printf '%s\n' 'bf compile did not create dist/test.long' >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
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"
|
rm -rf dist "$tmpdir"
|
||||||
|
|
||||||
output=$(./peppermint version)
|
output=$(./peppermint version)
|
||||||
|
|||||||
Reference in New Issue
Block a user