We are currently working on new rules for what content should and shouldn't be allowed on this website, and are looking for feedback! See Esolang:2026 topicality proposal to view and give feedback on the current draft.
Left-Right March/C Interpreter
Jump to navigation
Jump to search
#define _GNU_SOURCE
#include <assert.h>
#include <ctype.h>
#include <inttypes.h>
#include <stdbit.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct
{
char *content; // Not null terminated!
size_t size;
} field = {NULL, 0};
struct
{
char *pos;
enum
{
LEFT = -1,
RIGHT = 1,
} dir;
} field_ptr;
uint_fast8_t A;
void prog_error(const char *err_msg)
{
fprintf(stderr, "-----\nERROR\n-----\n%s\n", err_msg);
assert(field.content <= field_ptr.pos && field_ptr.pos < field.content + field.size);
ptrdiff_t fp_index = field_ptr.pos - field.content;
if (field_ptr.dir == LEFT && fp_index < 2)
{
for (ptrdiff_t i = 2; i > fp_index; i--)
{
fputc(' ', stderr);
}
}
fwrite(field.content, sizeof(*field.content), field.size, stderr);
fputc('\n', stderr);
switch (field_ptr.dir)
{
case LEFT:
for (const char *i = field.content; i < field_ptr.pos - 2; i++)
{
fputc(' ', stderr);
}
fputs("<<^", stderr);
break;
case RIGHT:
for (const char *i = field.content; i < field_ptr.pos; i++)
{
fputc(' ', stderr);
}
fputs("^>>", stderr);
break;
default:
unreachable();
}
free(field.content);
fprintf(stderr, "\nA = %.2hhu\n", A);
}
void printLSCEF(uint_least8_t c)
{
assert(c < 100);
if (c >= 98)
{
prog_error("Invalid character index");
exit(EXIT_FAILURE);
}
if (c == 66)
{
puts("\u00A3");
}
else
{
static const char chars[98] = "\n ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!\"\x00$%^&*(){}[]-=_+;'#~@:,./?><\\|`\t";
putchar(chars[c]);
}
}
uint_least8_t inputLSCEF(void)
{
begin:
char ascii = getchar();
if (feof(stdin))
{
return 99;
}
else if (ascii & 128)
{
char utf8[5] = {ascii};
utf8[fread(utf8 + 1, sizeof(char), stdc_leading_ones_uc(ascii), stdin) + 1] = 0;
static const char pound[] = "\u00A3";
if (strcmp(utf8, pound))
{
fprintf(stderr, "Cannot encode %s into LSCEF", utf8);
goto begin;
}
else
{
return 66;
}
}
else
{
static const uint_least8_t values[128] = {98, 98, 98, 98, 98, 98, 98, 98, 98, 97, 0, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 1, 64, 65, 84, 67, 68, 70, 83, 72, 73, 71, 81, 88, 78, 89, 90, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 87, 82, 93, 79, 92, 91, 86, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 76, 94, 77, 69, 80, 96, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 74, 95, 75, 85, 98}; // Reverse lookup for LSCEF
uint_least8_t ret = values[(unsigned)ascii];
if (ret == 98)
{
fprintf(stderr, "Cannot encode %c into LSCEF", ascii);
goto begin;
}
else
{
return ret;
}
}
}
int main(int argc, const char *const *argv)
{
// File handling
if (argc < 2)
{
fputs("Specify the file to interperet.\n", stderr);
return EXIT_FAILURE;
}
FILE *f = fopen(argv[1], "r");
if (f == NULL)
{
perror("Could not access the requested file");
return EXIT_FAILURE;
}
// Load the program
{
size_t dummy = 0;
field.size = getline(&field.content, &dummy, f);
}
if (ferror(f))
{
fclose(f);
free(field.content);
perror(NULL);
return EXIT_FAILURE;
}
else if (feof(f) && field.size == (size_t)-1)
{
field.size = 0;
}
fclose(f);
if (field.content[field.size] == '\n')
{
field.size--;
}
field.content = realloc(field.content, field.size);
for (const char *i = field.content; i < field.content + field.size; i++)
{
if (*i & 128)
{
free(field.content);
fputs("Non\u2010ASCII characters are not allowed in source code.\n", stderr);
return EXIT_FAILURE;
}
}
// Execute the program
field_ptr.pos = field.content;
field_ptr.dir = RIGHT;
while (field_ptr.pos >= field.content && field_ptr.pos < field.content + field.size)
{
if (isdigit(*field_ptr.pos))
{
field_ptr.pos += field_ptr.dir;
continue;
}
if (!(isdigit(field_ptr.pos[field_ptr.dir]) && isdigit(field_ptr.pos[2 * field_ptr.dir])) || field_ptr.pos + 2 * field_ptr.dir < field.content || field_ptr.pos + 2 * field_ptr.dir >= field.content + field.size)
{
prog_error("Invalid number for command");
return EXIT_FAILURE;
}
uint_fast8_t F;
switch (field_ptr.dir)
{
case LEFT:
F = 10 * (field_ptr.pos[-2] - '0') + (field_ptr.pos[-1] - '0');
break;
case RIGHT:
F = 10 * (field_ptr.pos[1] - '0') + (field_ptr.pos[2] - '0');
break;
default:
unreachable();
}
assert(A < 100 && F < 100);
switch (*field_ptr.pos)
{
case 'a':
A = (A + F) % 100;
break;
case 'b':
if (A)
{
field_ptr.pos += (F - 2) * field_ptr.dir; // Step-on-3s optimization requires this
}
break;
case 'c':
A = F;
break;
case 'd':
if (!F)
{
prog_error("Division by 0");
return EXIT_FAILURE;
}
A /= F;
break;
case 'i':
A = inputLSCEF();
break;
case 'j':
field_ptr.pos += (F - 2) * field_ptr.dir; // Step-on-3s optimization requires this
break;
case 'l':
field_ptr.dir = LEFT;
field_ptr.pos += 2; // Step-on-3s optimization requires this
break;
case 'm':
A = (A * F) % 100;
break;
case 'p':
printLSCEF(A);
break;
case 'r':
field_ptr.dir = RIGHT;
field_ptr.pos -= 2; // Step-on-3s optimization requires this
break;
case 's':
A = (A - F + 100) % 100;
break;
case 'w':
switch (field_ptr.dir)
{
case LEFT:
field_ptr.pos[-2] = A / 10;
field_ptr.pos[-1] = A % 10;
break;
case RIGHT:
field_ptr.pos[1] = A / 10;
field_ptr.pos[2] = A % 10;
break;
default:
unreachable();
}
break;
default:
prog_error("Unknown command");
return EXIT_FAILURE;
}
field_ptr.pos += 3 * field_ptr.dir; // Digits used by this instruction are guaranteed to NOP, so skip them entirely
}
free(field.content);
return EXIT_SUCCESS;
}