Brainfuck compiler
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

152 lines
3.3KB

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <stdint.h>
  5. #include <assert.h>
  6. #include <errno.h>
  7. #define exit_fatal(...) \
  8. do { \
  9. fprintf (stderr, "fatal: " __VA_ARGS__); \
  10. exit (EXIT_FAILURE); \
  11. } while (0)
  12. // --- Safe memory management --------------------------------------------------
  13. static void *
  14. xmalloc (size_t n)
  15. {
  16. void *p = malloc (n);
  17. if (!p)
  18. exit_fatal ("malloc: %s\n", strerror (errno));
  19. return p;
  20. }
  21. static void *
  22. xrealloc (void *o, size_t n)
  23. {
  24. void *p = realloc (o, n);
  25. if (!p && n)
  26. exit_fatal ("realloc: %s\n", strerror (errno));
  27. return p;
  28. }
  29. // --- Dynamically allocated strings -------------------------------------------
  30. struct str
  31. {
  32. char *str; ///< String data, null terminated
  33. size_t alloc; ///< How many bytes are allocated
  34. size_t len; ///< How long the string actually is
  35. };
  36. static void
  37. str_init (struct str *self)
  38. {
  39. self->alloc = 16;
  40. self->len = 0;
  41. self->str = strcpy (xmalloc (self->alloc), "");
  42. }
  43. static void
  44. str_ensure_space (struct str *self, size_t n)
  45. {
  46. // We allocate at least one more byte for the terminating null character
  47. size_t new_alloc = self->alloc;
  48. while (new_alloc <= self->len + n)
  49. new_alloc <<= 1;
  50. if (new_alloc != self->alloc)
  51. self->str = xrealloc (self->str, (self->alloc = new_alloc));
  52. }
  53. static void
  54. str_append_data (struct str *self, const void *data, size_t n)
  55. {
  56. str_ensure_space (self, n);
  57. memcpy (self->str + self->len, data, n);
  58. self->str[self->len += n] = '\0';
  59. }
  60. static void
  61. str_append_c (struct str *self, char c)
  62. {
  63. str_append_data (self, &c, 1);
  64. }
  65. // --- Main --------------------------------------------------------------------
  66. int
  67. main (int argc, char *argv[])
  68. {
  69. struct str program; str_init (&program);
  70. struct str data; str_init (&data);
  71. int c;
  72. while ((c = fgetc (stdin)) != EOF)
  73. str_append_c (&program, c);
  74. if (ferror (stdin))
  75. exit_fatal ("can't read program\n");
  76. FILE *input = fopen ("/dev/tty", "rb");
  77. if (!input)
  78. exit_fatal ("can't open terminal for reading\n");
  79. size_t *pairs = xmalloc (sizeof *pairs * program.len);
  80. size_t *stack = xmalloc (sizeof *stack * program.len);
  81. size_t nesting = 0;
  82. for (size_t i = 0; i < program.len; i++)
  83. {
  84. switch (program.str[i])
  85. {
  86. case '[':
  87. stack[nesting++] = i;
  88. break;
  89. case ']':
  90. assert (nesting > 0);
  91. --nesting;
  92. pairs[stack[nesting]] = i;
  93. pairs[i] = stack[nesting];
  94. }
  95. }
  96. assert (nesting == 0);
  97. size_t dataptr = 0;
  98. str_append_c (&data, 0);
  99. for (size_t i = 0; i < program.len; i++)
  100. {
  101. switch (program.str[i])
  102. {
  103. case '>':
  104. assert (dataptr != SIZE_MAX);
  105. if (++dataptr == data.len)
  106. str_append_c (&data, 0);
  107. break;
  108. case '<':
  109. assert (dataptr != 0);
  110. dataptr--;
  111. break;
  112. case '+': data.str[dataptr]++; break;
  113. case '-': data.str[dataptr]--; break;
  114. case '.':
  115. fputc (data.str[dataptr], stdout);
  116. break;
  117. case ',':
  118. data.str[dataptr] = c = fgetc (input);
  119. assert (c != EOF);
  120. break;
  121. case '[': if (!data.str[dataptr]) i = pairs[i]; break;
  122. case ']': if ( data.str[dataptr]) i = pairs[i]; break;
  123. default:
  124. break;
  125. }
  126. }
  127. return 0;
  128. }