Created
February 12, 2022 16:13
-
-
Save p7g/9a6d15291f42391cac24cd6f69011a30 to your computer and use it in GitHub Desktop.
Manually translated joe -> C, this BF implementations runs only 14% slower than bf.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// vim: ft=java | |
import joe.collections.ArrayList; | |
import joe.fs.File; | |
import joe.io.StdOut; | |
import joe.io.StdErr; | |
class Tape { | |
final ArrayList<uint> data; | |
uint pos; | |
Tape() { | |
data = new ArrayList<uint>(); | |
data.push(0); | |
pos = 0; | |
} | |
~Tape() { | |
delete data; | |
} | |
uint get() { | |
return data.get(pos); | |
} | |
void inc(int amount) { | |
data.set(pos, data.get(pos) + amount); | |
} | |
void move(int amount) { | |
pos += amount; | |
while (pos >= data.length()) | |
data.push(0); | |
} | |
} | |
interface Op { | |
void run(Tape tape); | |
} | |
class Inc implements Op { | |
final int amount; | |
Inc(int amount_) { | |
amount = amount_; | |
} | |
void run(Tape tape) { | |
tape.inc(amount); | |
} | |
} | |
class Move implements Op { | |
final int amount; | |
Move(int amount_) { | |
amount = amount_; | |
} | |
void run(Tape tape) { | |
tape.move(amount); | |
} | |
} | |
class Print implements Op { | |
void run(Tape tape) { | |
StdOut s = StdOut.get(); | |
s.writeByte(tape.get()); | |
s.flush(); | |
} | |
} | |
class Loop implements Op { | |
final Op[] ops; | |
Loop(Op[] ops_) { | |
ops = ops_; | |
} | |
~Loop() { | |
delete ops; | |
} | |
void run(Tape tape) { | |
// hack to dereference `this` pointer outside of loop | |
Op[] ops = this.ops; | |
while (tape.get() != 0) | |
Bf.run(ops, tape); | |
} | |
} | |
class Bf { | |
static Op[] parse(Iterator<char> it) { | |
ArrayList<Op> ops = new ArrayList<Op>(); | |
loop: | |
while (it.hasNext()) { | |
switch (it.next()) { | |
case '+': | |
ops.push(new Inc(1)); | |
break; | |
case '-': | |
ops.push(new Inc(-1)); | |
break; | |
case '>': | |
ops.push(new Move(1)); | |
break; | |
case '<': | |
ops.push(new Move(-1)); | |
break; | |
case '.': | |
ops.push(new Print()); | |
break; | |
case '[': | |
ops.push(new Loop(parse(it))); | |
break; | |
case ']': | |
break loop; | |
} | |
} | |
return ops.intoSlice(); | |
} | |
static void run(Op[] ops, Tape tape) { | |
for (int i = 0; i < ops.length(); i += 1) { | |
op.run(tape); | |
} | |
} | |
static int main(String[] args) { | |
if (args.length() < 2) { | |
StdErr.get().writeString("Expected filename arg\n"); | |
return 1; | |
} | |
File f = new File(args[1]); | |
Op[] ops = parse(f.iterator()); | |
delete f; | |
Tape tape = new Tape(); | |
run(ops, tape); | |
delete ops; | |
delete tape; | |
return 0; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdbool.h> | |
#include <stdio.h> | |
#include <stdint.h> | |
#include <stdlib.h> | |
// Forward declarations | |
typedef uint32_t joe_uint; | |
typedef int32_t joe_int; | |
typedef bool joe_bool; | |
typedef char joe_char; | |
typedef struct ArrayList_uint *ArrayList_uint; | |
typedef struct ArrayList_Op *ArrayList_Op; | |
typedef struct Tape *Tape; | |
typedef struct Op Op; | |
typedef struct slice_Op slice_Op; | |
typedef struct Iterator_char Iterator_char; | |
typedef struct String String; | |
typedef struct slice_String slice_String; | |
typedef struct slice_uint slice_uint; | |
typedef struct FileIterator *FileIterator; | |
// Transparent classes (i.e. 1 final field), need to somehow sort by dependency | |
typedef joe_int Inc; | |
typedef joe_int Move; | |
typedef void Print; | |
typedef slice_Op *Loop; // it's a pointer because Loop implements an interface | |
typedef FILE *File; | |
typedef joe_char *Pointer_char; | |
typedef joe_uint *Pointer_uint; | |
typedef Pointer_char StaticString; | |
typedef Op *Pointer_Op; | |
typedef String *Pointer_String; | |
// vtables | |
struct Op_vtable { | |
void (*destructor)(void *); | |
void (*run)(void *, Tape); | |
}; | |
struct Iterator_char_vtable { | |
void (*destructor)(void *); | |
joe_bool (*hasNext)(void *); | |
joe_char (*next)(void *); | |
}; | |
struct String_vtable { | |
void (*destructor)(void *); | |
Pointer_char (*asPointer)(void *); | |
}; | |
// interface struct declarations | |
struct Op { | |
void *data; | |
struct Op_vtable *vtable; | |
}; | |
struct String { | |
void *data; | |
struct String_vtable *vtable; | |
}; | |
struct Iterator_char { | |
void *data; | |
struct Iterator_char_vtable *vtable; | |
}; | |
// class struct declarations | |
struct slice_uint { | |
joe_uint length; | |
joe_uint *data; | |
}; | |
struct slice_Op { | |
joe_uint length; | |
Op *data; | |
}; | |
struct slice_String { | |
joe_uint length; | |
String *data; | |
}; | |
struct ArrayList_uint { | |
joe_uint length; | |
slice_uint data; | |
}; | |
struct ArrayList_Op { | |
joe_uint length; | |
slice_Op data; | |
}; | |
struct Tape { | |
ArrayList_uint data; | |
joe_uint pos; | |
}; | |
struct FileIterator { | |
File f; | |
joe_int peeked; | |
}; | |
// Method prototypes | |
static void Tape_constructor(Tape *); | |
static void Tape_destructor(Tape); | |
static joe_uint Tape_get(Tape); | |
static void Tape_inc(Tape, joe_int); | |
static void Tape_move(Tape, joe_int); | |
static void Inc_constructor(Inc *, joe_int); | |
static void Inc_destructor(Inc); | |
static void Inc_run(Inc, Tape); | |
static void Move_constructor(Move *, joe_int); | |
static void Move_destructor(Move); | |
static void Move_run(Move, Tape); | |
static void Print_destructor(void *); | |
static void Print_run(void *, Tape); | |
static void Loop_constructor(Loop *, slice_Op); | |
static void Loop_destructor(Loop); | |
static void Loop_run(Loop, Tape); | |
static void ArrayList_uint_constructor(ArrayList_uint *); | |
static void ArrayList_uint_destructor(ArrayList_uint); | |
static joe_uint ArrayList_uint_get(ArrayList_uint, joe_uint); | |
static void ArrayList_uint_set(ArrayList_uint, joe_uint, joe_uint); | |
static joe_uint ArrayList_uint_length(ArrayList_uint); | |
static void ArrayList_uint_push(ArrayList_uint, joe_uint); | |
static void ArrayList_Op_constructor(ArrayList_Op *); | |
static void ArrayList_Op_push(ArrayList_Op, Op); | |
static slice_Op ArrayList_Op_intoSlice(ArrayList_Op); | |
static void slice_Op_constructor(slice_Op *, Pointer_Op, joe_uint); | |
static void slice_Op_destructor(slice_Op); | |
static joe_uint slice_Op_length(slice_Op); | |
static slice_Op slice_Op_extend(slice_Op, joe_uint); | |
static void slice_uint_constructor(slice_uint *, Pointer_uint, joe_uint); | |
static void slice_uint_destructor(slice_uint); | |
static joe_uint slice_uint_length(slice_uint); | |
static slice_uint slice_uint_extend(slice_uint, joe_uint); | |
static void slice_String_constructor(slice_String *, Pointer_String, joe_uint); | |
static void slice_String_destructor(slice_String); | |
static joe_uint slice_String_length(slice_String); | |
static void StaticString_constructor(StaticString *, Pointer_char); | |
static void StaticString_destructor(StaticString); | |
static Pointer_char StaticString_asPointer(StaticString); | |
static void File_constructor(File *, String); | |
static void File_destructor(File); | |
static FileIterator File_iterator(File); | |
static void FileIterator_constructor(FileIterator *, File); | |
static void FileIterator_destructor(FileIterator); | |
static joe_bool FileIterator_hasNext(FileIterator); | |
static joe_char FileIterator_next(FileIterator); | |
static slice_Op Bf_parse(Iterator_char); | |
static void Bf_run(slice_Op, Tape); | |
static int Bf_main(slice_String); | |
// vtable declarations | |
static struct String_vtable StaticString_String_vtable = { | |
(void *) StaticString_destructor, | |
(void *) StaticString_asPointer, | |
}; | |
static struct Op_vtable Inc_Op_vtable = { | |
(void *) Inc_destructor, | |
(void *) Inc_run, | |
}; | |
static struct Op_vtable Move_Op_vtable = { | |
(void *) Move_destructor, | |
(void *) Move_run, | |
}; | |
static struct Op_vtable Print_Op_vtable = { | |
(void *) Print_destructor, | |
(void *) Print_run, | |
}; | |
static struct Op_vtable Loop_Op_vtable = { | |
(void *) Loop_destructor, | |
(void *) Loop_run, | |
}; | |
static struct Iterator_char_vtable FileIterator_Iterator_char_vtable = { | |
(void *) FileIterator_destructor, | |
(void *) FileIterator_hasNext, | |
(void *) FileIterator_next, | |
}; | |
// method implementations | |
static void Tape_constructor(Tape *this) | |
{ | |
*this = malloc(sizeof(**this)); | |
ArrayList_uint_constructor(&(*this)->data); | |
ArrayList_uint_push((*this)->data, 0); | |
(*this)->pos = 0; | |
} | |
static void Tape_destructor(Tape this) | |
{ | |
ArrayList_uint_destructor(this->data); | |
free(this); | |
} | |
static joe_uint Tape_get(Tape this) | |
{ | |
return ArrayList_uint_get(this->data, this->pos); | |
} | |
static void Tape_inc(Tape this, joe_int amount) | |
{ | |
ArrayList_uint_set(this->data, | |
this->pos, | |
ArrayList_uint_get(this->data, this->pos) + amount); | |
} | |
static void Tape_move(Tape this, joe_int amount) | |
{ | |
this->pos += amount; | |
while (this->pos >= ArrayList_uint_length(this->data)) | |
ArrayList_uint_push(this->data, 0); | |
} | |
static void Inc_constructor(Inc *this, joe_int amount) | |
{ | |
*this = amount; | |
} | |
static void Inc_destructor(Inc this) | |
{ | |
(void) this; | |
} | |
static void Inc_run(Inc this, Tape tape) | |
{ | |
Tape_inc(tape, this); | |
} | |
static void Move_constructor(Move *this, joe_int amount) | |
{ | |
*this = amount; | |
} | |
static void Move_destructor(Move this) | |
{ | |
(void) this; | |
} | |
static void Move_run(Move this, Tape tape) | |
{ | |
Tape_move(tape, this); | |
} | |
static void Print_destructor(void *this) | |
{ | |
} | |
static void Print_run(void *this, Tape tape) | |
{ | |
(void) this; | |
// FIXME: cheating | |
putchar(Tape_get(tape)); | |
fflush(stdout); | |
} | |
static void Loop_constructor(Loop *this, slice_Op ops) | |
{ | |
*this = malloc(sizeof(**this)); | |
(*this)->data = ops.data; | |
(*this)->length = ops.length; | |
} | |
static void Loop_destructor(Loop this) | |
{ | |
slice_Op_destructor(*this); | |
free(this); | |
} | |
static void Loop_run(Loop this, Tape tape) | |
{ | |
slice_Op ops = *this; | |
while (Tape_get(tape) != 0) | |
Bf_run(ops, tape); | |
} | |
static void ArrayList_uint_constructor(ArrayList_uint *this) | |
{ | |
*this = malloc(sizeof(**this)); | |
(*this)->length = 0; | |
slice_uint_constructor(&(*this)->data, NULL, 0); | |
} | |
static void ArrayList_uint_destructor(ArrayList_uint this) | |
{ | |
slice_uint_destructor(this->data); | |
free(this); | |
} | |
static joe_uint ArrayList_uint_get(ArrayList_uint this, joe_uint idx) | |
{ | |
// FIXME: slice ops directly translated? bounds checks? | |
return this->data.data[idx]; | |
} | |
static void ArrayList_uint_set(ArrayList_uint this, joe_uint idx, joe_uint val) | |
{ | |
this->data.data[idx] = val; | |
} | |
static joe_uint ArrayList_uint_length(ArrayList_uint this) | |
{ | |
return this->length; | |
} | |
static void ArrayList_uint_push(ArrayList_uint this, joe_uint val) | |
{ | |
if (this->length == slice_uint_length(this->data)) { | |
joe_uint cap = slice_uint_length(this->data); | |
if (cap == 0) | |
cap = 4; | |
this->data = slice_uint_extend(this->data, cap << 1); | |
} | |
this->data.data[this->length++] = val; | |
} | |
static void ArrayList_Op_constructor(ArrayList_Op *this) | |
{ | |
*this = malloc(sizeof(**this)); | |
(*this)->length = 0; | |
slice_Op_constructor(&(*this)->data, NULL, 0); | |
} | |
static void ArrayList_Op_push(ArrayList_Op this, Op val) | |
{ | |
if (this->length == slice_Op_length(this->data)) { | |
joe_uint cap = slice_Op_length(this->data); | |
if (cap == 0) | |
cap = 4; | |
this->data = slice_Op_extend(this->data, cap << 1); | |
} | |
this->data.data[this->length++] = val; | |
} | |
static slice_Op ArrayList_Op_intoSlice(ArrayList_Op this) | |
{ | |
slice_Op data = this->data; | |
data.length = this->length; | |
free(this); | |
return data; | |
} | |
static void slice_Op_constructor(slice_Op *this, Pointer_Op data, joe_uint length) | |
{ | |
this->data = data; | |
this->length = length; | |
} | |
static void slice_Op_destructor(slice_Op this) | |
{ | |
for (joe_int i = 0; i < this.length; i += 1) { | |
Op item = this.data[i]; | |
item.vtable->destructor(item.data); | |
} | |
if (this.data) | |
free(this.data); | |
} | |
static joe_uint slice_Op_length(slice_Op this) | |
{ | |
return this.length; | |
} | |
static slice_Op slice_Op_extend(slice_Op this, joe_uint length) | |
{ | |
slice_Op newSlice; | |
newSlice.data = realloc(this.data, sizeof(*this.data) * length); | |
newSlice.length = length; | |
return newSlice; | |
} | |
static void slice_uint_constructor(slice_uint *this, Pointer_uint data, joe_uint length) | |
{ | |
this->data = data; | |
this->length = length; | |
} | |
static void slice_uint_destructor(slice_uint this) | |
{ | |
free(this.data); | |
} | |
static joe_uint slice_uint_length(slice_uint this) | |
{ | |
return this.length; | |
} | |
static slice_uint slice_uint_extend(slice_uint this, joe_uint length) | |
{ | |
slice_uint newSlice; | |
newSlice.data = realloc(this.data, sizeof(*this.data) * length); | |
newSlice.length = length; | |
return newSlice; | |
} | |
static void slice_String_constructor(slice_String *this, Pointer_String data, joe_uint length) | |
{ | |
this->data = data; | |
this->length = length; | |
} | |
static void slice_String_destructor(slice_String this) | |
{ | |
for (joe_int i = 0; i < this.length; i += 1) { | |
String item = this.data[i]; | |
item.vtable->destructor(item.data); | |
} | |
if (this.data) | |
free(this.data); | |
} | |
static joe_uint slice_String_length(slice_String this) | |
{ | |
return this.length; | |
} | |
static void StaticString_constructor(StaticString *this, Pointer_char str) | |
{ | |
*this = str; | |
} | |
static void StaticString_destructor(StaticString this) | |
{ | |
(void) this; | |
} | |
static Pointer_char StaticString_asPointer(StaticString this) | |
{ | |
return this; | |
} | |
static void File_constructor(File *this, String name) | |
{ | |
*this = fopen(name.vtable->asPointer(name.data), "r"); | |
} | |
static void File_destructor(File this) | |
{ | |
if (this) | |
fclose(this); | |
} | |
static FileIterator File_iterator(File this) | |
{ | |
FileIterator it; | |
FileIterator_constructor(&it, this); | |
return it; | |
} | |
static void FileIterator_constructor(FileIterator *this, File file) | |
{ | |
*this = malloc(sizeof(**this)); | |
(*this)->f = file; | |
(*this)->peeked = -1; | |
} | |
static void FileIterator_destructor(FileIterator this) | |
{ | |
free(this); | |
} | |
static joe_bool FileIterator_hasNext(FileIterator this) | |
{ | |
if (this->peeked != -1) | |
return true; | |
this->peeked = fgetc(this->f); | |
return !feof(this->f); | |
} | |
static joe_char FileIterator_next(FileIterator this) | |
{ | |
if (this->peeked != -1) { | |
joe_char c = this->peeked; | |
this->peeked = -1; | |
return c; | |
} | |
return fgetc(this->f); | |
} | |
static slice_Op Bf_parse(Iterator_char it) | |
{ | |
ArrayList_Op ops; | |
ArrayList_Op_constructor(&ops); | |
while (it.vtable->hasNext(it.data)) { | |
switch (it.vtable->next(it.data)) { | |
case '+': { | |
Inc tmp1; | |
Inc_constructor(&tmp1, 1); | |
union { | |
void *as_ptr; | |
int as_int; | |
} data; | |
data.as_int = tmp1; | |
Op op; | |
op.data = data.as_ptr; | |
op.vtable = &Inc_Op_vtable; | |
ArrayList_Op_push(ops, op); | |
break; | |
} | |
case '-': { | |
Inc tmp1; | |
Inc_constructor(&tmp1, -1); | |
union { | |
void *as_ptr; | |
int as_int; | |
} data; | |
data.as_int = tmp1; | |
Op op; | |
op.data = data.as_ptr; | |
op.vtable = &Inc_Op_vtable; | |
ArrayList_Op_push(ops, op); | |
break; | |
} | |
case '>': { | |
Move tmp1; | |
Move_constructor(&tmp1, 1); | |
union { | |
void *as_ptr; | |
int as_int; | |
} data; | |
data.as_int = tmp1; | |
Op op; | |
op.data = data.as_ptr; | |
op.vtable = &Move_Op_vtable; | |
ArrayList_Op_push(ops, op); | |
break; | |
} | |
case '<': { | |
Move tmp1; | |
Move_constructor(&tmp1, -1); | |
union { | |
void *as_ptr; | |
int as_int; | |
} data; | |
data.as_int = tmp1; | |
Op op; | |
op.data = data.as_ptr; | |
op.vtable = &Move_Op_vtable; | |
ArrayList_Op_push(ops, op); | |
break; | |
} | |
case '.': { | |
Op op; | |
op.data = NULL; | |
op.vtable = &Print_Op_vtable; | |
ArrayList_Op_push(ops, op); | |
break; | |
} | |
case '[': { | |
Loop tmp1; | |
Loop_constructor(&tmp1, Bf_parse(it)); | |
Op op; | |
op.data = tmp1; | |
op.vtable = &Loop_Op_vtable; | |
ArrayList_Op_push(ops, op); | |
break; | |
} | |
case ']': | |
goto end_loop; | |
} | |
} | |
end_loop: | |
return ArrayList_Op_intoSlice(ops); | |
} | |
static void Bf_run(slice_Op ops, Tape tape) | |
{ | |
for (joe_int i = 0; i < slice_Op_length(ops); i += 1) { | |
Op tmp1 = ops.data[i]; | |
tmp1.vtable->run(tmp1.data, tape); | |
} | |
} | |
static int Bf_main(slice_String argv) | |
{ | |
if (slice_String_length(argv) < 2) { | |
fputs("Expected filename arg\n", stderr); | |
return 1; | |
} | |
File f; | |
File_constructor(&f, argv.data[1]); | |
if (!f) { | |
perror("fopen"); | |
return 1; | |
} | |
FileIterator tmp1 = File_iterator(f); | |
Iterator_char tmp2; | |
tmp2.data = tmp1; | |
tmp2.vtable = &FileIterator_Iterator_char_vtable; | |
slice_Op ops = Bf_parse(tmp2); | |
FileIterator_destructor(tmp1); | |
File_destructor(f); | |
Tape tape; | |
Tape_constructor(&tape); | |
Bf_run(ops, tape); | |
slice_Op_destructor(ops); | |
Tape_destructor(tape); | |
return 0; | |
} | |
int main(int argc, char **argv) | |
{ | |
slice_String argv_slice; | |
slice_String_constructor(&argv_slice, malloc(sizeof(String) * argc), argc); | |
for (joe_int i = 0; i < argc; i += 1) { | |
StaticString s; | |
StaticString_constructor(&s, argv[i]); | |
argv_slice.data[i].data = s; | |
argv_slice.data[i].vtable = &StaticString_String_vtable; | |
} | |
joe_int retval = Bf_main(argv_slice); | |
slice_String_destructor(argv_slice); | |
return retval; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment