First commit

This commit is contained in:
Maurizio Porrato 2020-09-04 11:05:16 +01:00
commit 86194ae081
14 changed files with 738 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*[~%]
*.o
core
turing

24
Makefile Normal file
View File

@ -0,0 +1,24 @@
.POSIX:
.PHONY: clean all pretty
DEFINES=-D_XOPEN_SOURCE=500
CFLAGS=-std=c99 -Wall -pedantic $(DEFINES)
RM=-rm -f
BINARIES=turing
OFILES=turing.o table.o program.o tape.o
all: $(BINARIES)
turing.o: turing.c table.h program.h tape.h
table.o: table.c table.h
program.o: program.c program.h table.h
tape.o: tape.c tape.h table.h
turing: $(OFILES)
$(CC) $(LDFLAGS) -o $@ $(OFILES) $(LDLIBS)
pretty:
astyle --style=mozilla *.c *.h
clean:
$(RM) *% *~ *.o *.orig core $(BINARIES)

121
program.c Normal file
View File

@ -0,0 +1,121 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "table.h"
#include "program.h"
static void *row_clone(void *p)
{
struct instruction *row;
row = (struct instruction *)malloc(sizeof(struct instruction));
memcpy((void *)row, p, sizeof(struct instruction));
return (void *)row;
}
static int row_cmp(void *a, void *b)
{
struct instruction *ra, *rb;
ra = (struct instruction *)a;
rb = (struct instruction *)b;
if (ra->state < rb->state)
return -1;
if (ra->state > rb->state)
return 1;
if (ra->symbol < rb->symbol)
return -1;
if (ra->symbol > rb->symbol)
return 1;
return 0;
}
static void row_repr(void *p, char *buf, int size)
{
struct instruction *row;
static char *moves = "LNR";
row = (struct instruction *)p;
snprintf(buf, size, "[%d %d | %d %d %c]",
row->state, row->symbol, row->nextstate, row->write,
((row->move >= -1) && (row->move <=1) ? moves[1+row->move] : '?'));
buf[size-1] = '\0';
}
static short int parse_move(char *m)
{
char c;
c = (char)tolower(m[0]);
if (c == 'l')
return -1;
if (c == 'r')
return 1;
if (c == 'n')
return 0;
return atoi(m);
}
struct table *program_new()
{
return table_new(20, 10, row_clone, free, row_cmp, row_repr);
}
int program_load(FILE *f, struct table *prg, struct table *st, struct table *sym)
{
char line[LINE_LEN+1];
char state[STATE_LEN+1];
char symbol[SYMBOL_LEN+1];
char write[SYMBOL_LEN+1];
char move[8];
char nextstate[STATE_LEN+1];
int ll;
char *l;
char *p;
int r;
struct instruction row;
int cnt;
line[LINE_LEN] = '\0';
cnt = 0;
for (;;) {
l = fgets(line, sizeof(line)-1, f);
if (l == NULL)
break;
ll = strlen(l);
if (ll > 0) {
p = &line[ll-1];
while ((ll > 0) && ((*p == '\r') || (*p == '\n'))) {
*p = '\0';
ll--;
}
}
p = strchr(l, '#');
if (p != NULL)
*p = '\0';
r = sscanf(l, "%s %s %s %s %s",
state, symbol, nextstate, write, move);
if (r == 0)
continue;
if (r != 5) {
printf("Parsing error!\n");
} else {
row.state = table_insert(st, state);
row.symbol = table_insert(sym, symbol);
row.write = table_insert(sym, write);
row.move = parse_move(move);
row.nextstate = table_insert(st, nextstate);
table_insert(prg, &row);
cnt++;
}
}
return cnt;
}

26
program.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef _PROGRAM_H
#define _PROGRAM_H
#include <stdlib.h>
#include <stdio.h>
#include "table.h"
#include "program.h"
#define LINE_LEN 100
#define SYMBOL_LEN 20
#define STATE_LEN 20
struct instruction
{
int state;
int symbol;
int write;
short int move;
int nextstate;
};
struct table *program_new();
int program_load(FILE *f, struct table *prg, struct table *st, struct table *sym);
#endif

9
readme.md Normal file
View File

@ -0,0 +1,9 @@
A simple and portable Turing Machine simulator
==============================================
This code should build cleanly on any POSIX OS using a C99 compliant
C compiler.
The goal of this project is to be easily understandable and hackable,
and is not meant to be efficient.

6
samples/binincr.program Normal file
View File

@ -0,0 +1,6 @@
start nul carry nul L
start 0 start 0 R
start 1 start 1 R
carry nul halt 1 N
carry 0 halt 1 N
carry 1 carry 0 L

1
samples/binincr.tape.1 Normal file
View File

@ -0,0 +1 @@
1 1 0 0 1 0 1 1

View File

@ -0,0 +1,6 @@
A 0 B 1 R
A 1 C 1 L
B 0 A 1 L
B 1 B 1 R
C 0 B 1 L
C 1 halt 1 N

View File

@ -0,0 +1 @@

151
table.c Normal file
View File

@ -0,0 +1,151 @@
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "table.h"
struct table *table_new(int capacity, int increment,
item_clone_f clone_f, item_free_f free_f, item_compare_f compare_f, item_repr_f repr_f)
{
struct table *r;
r = (struct table *)malloc(sizeof(*r));
if (r != NULL) {
r->items = (void **)malloc(capacity*sizeof(void *));
if (r->items == NULL) {
free(r);
return NULL;
}
r->indexes = (int *)malloc(capacity*sizeof(int));
if (r->indexes == NULL) {
free(r->items);
free(r);
return NULL;
}
r->size = 0;
r->capacity = capacity;
r->increment = increment;
r->clone_f = clone_f;
r->free_f = free_f;
r->compare_f = compare_f;
r->repr_f = repr_f;
r->dirty = true;
r->lookup = NULL;
}
return r;
}
void table_free(struct table *t)
{
if (t->lookup != NULL)
free(t->lookup);
if (t->free_f != NULL)
for (int i=0; i < t->size; i++)
t->free_f(t->items[i]);
free(t->indexes);
free(t->items);
free(t);
}
static bool table_find_internal(struct table *t, void *entry, int *pos)
{
int cmp;
for (*pos=0; *pos < t->size; (*pos)++) {
cmp = t->compare_f(entry, t->items[*pos]);
if (cmp == 0)
return true;
if (cmp < 0)
return false;
}
return false;
}
int table_find(struct table *t, void *entry)
{
int pos;
if (table_find_internal(t, entry, &pos))
return t->indexes[pos];
else
return -1;
}
int table_insert(struct table *t, void *entry)
{
bool found;
int pos;
void **newitems;
int *newindexes;
void *swap;
int swapidx;
int r;
int newcapacity;
found = table_find_internal(t, entry, &pos);
if (!found) {
if (t->capacity <= t->size) {
newcapacity = t->capacity + t->increment;
newitems = (void **) realloc(
t->items, newcapacity*sizeof(void *));
if (newitems == NULL)
return -1;
newindexes = (int *) realloc(
t->indexes, newcapacity*sizeof(int));
if (newindexes == NULL) {
t->items = (void **) realloc(
newitems, t->capacity*sizeof(void *));
return -1;
}
t->items = newitems;
t->indexes = newindexes;
t->capacity = newcapacity;
}
r = t->size;
if (t->clone_f != NULL)
entry = t->clone_f(entry);
for (int i=pos; i <= t->size; i++) {
swap = t->items[i];
swapidx = t->indexes[i];
t->items[i] = entry;
t->indexes[i] = r;
entry = swap;
r = swapidx;
}
t->size++;
t->dirty = true;
}
return t->indexes[pos];
}
void *table_lookup(struct table *t, int idx)
{
if (t->dirty) {
t->lookup = realloc(t->lookup, t->size*sizeof(void *));
// TODO: check allocation
for (int i=0; i<t->size; i++)
t->lookup[t->indexes[i]] = t->items[i];
t->dirty = false;
}
return t->lookup[idx];
}
void table_print(struct table *t)
{
char buf[200];
printf("size=%d capacity=%d increment=%d\n", t->size, t->capacity, t->increment);
for (int i=0; i<t->size; i++) {
if (t->repr_f != NULL)
t->repr_f(t->items[i], buf, sizeof(buf));
printf(" %d (%d) %s\n", i, t->indexes[i],
(t->repr_f != NULL ? buf : "???"));
}
}

34
table.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef __TABLE_H
#define __TABLE_H
#include <stdbool.h>
typedef void *(*item_clone_f)(void *);
typedef void (*item_free_f)(void *);
typedef int (*item_compare_f)(void *, void *);
typedef void (*item_repr_f)(void *, char *, int);
struct table
{
int size; // Number of entries actually used
int capacity; // Total allocated number of entries
int increment; // How many additional entries to allocate when growing the table
item_clone_f clone_f;
item_free_f free_f;
item_compare_f compare_f;
item_repr_f repr_f;
void **items; // Table entries, sorted
int *indexes; // Maps entried ids to their index inside the items array
void **lookup; // Cached lookup results
bool dirty; // Cached lookup entries are not up to date
};
struct table *table_new(int capacity, int increment,
item_clone_f clone_f, item_free_f free_f, item_compare_f compare_f, item_repr_f repr_f);
void table_free(struct table *t);
int table_find(struct table *t, void *entry);
int table_insert(struct table *t, void *entry);
void *table_lookup(struct table *t, int idx);
void table_print(struct table *t);
#endif

167
tape.c Normal file
View File

@ -0,0 +1,167 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "tape.h"
struct tape *tape_new(int blank)
{
struct tape *r;
r = (struct tape *)malloc(sizeof(struct tape));
if (r != NULL) {
r->head = 0;
r->blank = blank;
r->ncapacity = r->pcapacity = 0;
r->nsyms = r->psyms = NULL;
}
return r;
}
int tape_read(struct tape *t, int pos)
{
if (pos >= 0) {
if (pos >= t->pcapacity)
return t->blank;
else
return t->psyms[pos];
} else {
pos = -1 - pos;
if (pos >= t->ncapacity)
return t->blank;
else
return t->nsyms[pos];
}
}
static bool tape_grow(struct tape *t, int pos)
{
int **side;
int *capacity;
int newcapacity;
int *newside;
if (pos >= 0) {
side = &t->psyms;
capacity = &t->pcapacity;
} else {
pos = -1 - pos;
side = &t->nsyms;
capacity = &t->ncapacity;
}
if (pos + 1 < *capacity)
return true;
newcapacity = pos + TAPE_CHUNK_SIZE;
printf("Grow tape to %d\n", newcapacity);
newside = (int *)realloc(*side, newcapacity*sizeof(int));
if (newside == NULL)
return false;
*side = newside;
while (*capacity < newcapacity)
(*side)[(*capacity)++] = t->blank;
return true;
}
void tape_write(struct tape *t, int pos, int sym)
{
if (pos >= 0) {
if (pos >= t->pcapacity) {
if (sym == t->blank)
return;
if (!tape_grow(t, pos)) // TODO: error out
return;
}
t->psyms[pos] = sym;
} else {
pos = -1 - pos;
if (pos >= t->ncapacity) {
if (sym == t->blank)
return;
if (!tape_grow(t, -1 - pos)) // TODO: error out
return;
}
t->nsyms[pos] = sym;
}
}
int tape_load(struct tape *t, FILE *f, int offset, struct table *symbols)
{
char line[1000];
char *l;
int ll;
char *tok;
char *toksave;
static char *delim = " \t\f\n\r";
int counter = 0;
for (;;) {
l = fgets(line, sizeof(line)-1, f);
if (l == NULL)
break;
ll = strlen(l);
if (ll > 0) {
tok = strtok_r(line, delim, &toksave);
while (tok != NULL) {
tape_write(t, offset+counter, table_insert(symbols, tok));
counter++;
tok = strtok_r(NULL, delim, &toksave);
}
}
}
return counter;
}
#define HL_ON "\x01b[7m"
#define HL_OFF "\x01b[0m"
void tape_print(struct tape *t, struct table *sym)
{
int si;
int first, last;
printf("TAPE: ");
if ((t->ncapacity == 0) && (t->pcapacity == 0)) {
printf("(empty)\n");
return;
}
first = t->pcapacity;
last = -1-t->ncapacity;
for (int i=-1-t->ncapacity; i<t->pcapacity; i++) {
si = tape_read(t, i);
if ((si != t->blank) || (i == t->head)) {
if (i < first)
first = i;
if (i > last)
last = i;
}
}
if ((first == last) && (tape_read(t, first) == t->blank)) {
printf("(empty)\n");
return;
}
for (int i=first; i <= last; i++) {
if (i == t->head)
printf(HL_ON);
si = tape_read(t, i);
if (sym == NULL)
printf("%d", si);
else
printf("%s", (char *)table_lookup(sym, si));
if (i == t->head)
printf(HL_OFF);
printf(" ");
}
printf("\n");
}

24
tape.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef _TAPE_H
#define _TAPE_H
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include "table.h"
#define TAPE_CHUNK_SIZE 100
struct tape
{
int head; // Head position
int blank; // Blank symbol
int *nsyms, *psyms; // Halves of the infinite tape for negative and positive indices
int ncapacity, pcapacity; // Capacities of the two tape halves
};
struct tape *tape_new(int blank);
int tape_read(struct tape *t, int pos);
void tape_write(struct tape *t, int pos, int sym);
int tape_load(struct tape *t, FILE *f, int offset, struct table *symbols);
void tape_print(struct tape *t, struct table *sym);
#endif

164
turing.c Normal file
View File

@ -0,0 +1,164 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include "table.h"
#include "tape.h"
#include "program.h"
struct turing
{
struct table *states;
struct table *symbols;
struct table *program;
};
void *str_clone(void *p)
{
return (void *)strdup((char *)p);
}
int str_cmp(void *a, void *b)
{
return strcmp((char *)a, (char *)b);
}
void str_repr(void *p, char *buf, int size)
{
strncpy(buf, (char *)p, size-1);
buf[size-1] = '\0';
}
void turing_free(struct turing *tm)
{
if (tm == NULL)
return;
if (tm->program != NULL)
table_free(tm->program);
if (tm->symbols != NULL)
table_free(tm->symbols);
if (tm->states != NULL)
table_free(tm->states);
free(tm);
}
struct turing *turing_new(char *programfn, char *start, char *blank)
{
struct turing *r;
FILE *programf;
programf = fopen(programfn, "r");
if (programf == NULL)
return NULL;
r = (struct turing *)malloc(sizeof(struct turing));
if (r == NULL)
return NULL;
r->states = r->symbols = r->program = NULL;
r->states = table_new(20, 10, str_clone, free, str_cmp, str_repr);
r->symbols = table_new(20, 10, str_clone, free, str_cmp, str_repr);
r->program = program_new();
if ((r->states == NULL) || (r->symbols == NULL) || (r->program == NULL)) {
turing_free(r);
return NULL;
}
if (start != NULL)
table_insert(r->states, start);
if (blank != NULL)
table_insert(r->symbols, blank);
program_load(programf, r->program, r->states, r->symbols);
fclose(programf);
printf("STATES: ");
table_print(r->states);
printf("SYMBOLS: ");
table_print(r->symbols);
printf("PROGRAM: ");
table_print(r->program);
return r;
}
int turing_run(struct turing *tm, char *tapefn)
{
FILE *tapef;
struct instruction row, *insn;
int iinsn;
int steps;
struct tape *tape;
tapef = fopen(tapefn, "r");
if (tapef == NULL)
return EXIT_FAILURE;
tape = tape_new(0);
tape_load(tape, tapef, 0, tm->symbols);
fclose(tapef);
steps = 0;
row.state = 0;
for (;;) {
tape_print(tape, tm->symbols);
row.symbol = tape_read(tape, tape->head);
iinsn = table_find(tm->program, &row);
if (iinsn < 0)
break;
insn = table_lookup(tm->program, iinsn);
row.state = insn->nextstate;
tape_write(tape, tape->head, insn->write);
tape->head += insn->move;
steps++;
}
return steps;
}
int main(int argc, char *argv[])
{
int opt;
char *start, *blank;
struct turing *tm;
start = blank = NULL;
while ((opt = getopt(argc, argv, "s:b:")) != -1) {
switch (opt) {
case 's':
start = optarg;
break;
case 'b':
blank = optarg;
break;
default:
fprintf(stderr, "Usage: %s [-s start] [-b blank] program tape\n",
argv[0]);
return EXIT_FAILURE;
}
}
if (optind >= argc - 1) {
fprintf(stderr, "Expected arguments\n");
return EXIT_FAILURE;
}
tm = turing_new(argv[optind], start, blank);
if (tm == NULL) {
fprintf(stderr, "Error loading program.\n");
return EXIT_FAILURE;
}
turing_run(tm, argv[optind+1]);
turing_free(tm);
return EXIT_SUCCESS;
}