"git@gitlab.fit.cvut.cz:pusradek/bakalarka.git" did not exist on "465003669b2ab09a7d14b2e0b9b73eeccaec9348"
Newer
Older
#include <stdio.h>
#include <inttypes.h>
#include <assert.h>
void ostack_init ( OperandStack * stack ) {
stack -> elements = (Value**) malloc ( OPERAND_STACK_INIT_CAPACITY * sizeof (Value*) );
stack -> size = 0;
stack -> capacity = OPERAND_STACK_INIT_CAPACITY;
}
Michal Štěpánek
committed
Value * ostack_pop ( OperandStack * stack ) {
assert ( stack -> size );
stack -> size--;
Michal Štěpánek
committed
return stack -> elements [ stack -> size ];
}
Value * ostack_top ( OperandStack * stack ) {
return stack -> elements [ stack -> size - 1 ];
}
void ostack_push ( OperandStack * stack , Value * value ) {
if ( stack -> size == stack -> capacity ) {
Value ** tmp = (Value**) malloc ( 2 * stack -> capacity * sizeof (Value*) );
memcpy ( tmp, stack -> elements, stack -> size * sizeof (Value*) );
free ( stack -> elements );
stack -> elements = tmp;
}
stack -> elements [ stack -> size++ ] = value;
}
void ostack_destroy ( OperandStack * stack ) {
free ( stack -> elements );
}
void vm_init ( VM * vm, size_t heap_size ) {
heap_init ( & vm -> heap, heap_size );
make_null ( & vm -> heap );
Michal Štěpánek
committed
ostack_init ( & vm -> stack );
vm -> local_frame = NULL;
vm -> instruction_pointer = 0;
}
void vm_destroy ( VM * vm ) {
heap_destroy ( & vm -> heap );
Michal Štěpánek
committed
ostack_destroy ( & vm -> stack );
for ( size_t i = 0; i < vm -> program . constant_count; ++i ) {
Constant constant = vm -> program . constant_pool [ i ];
if ( constant . kind == CONSTANT_CLASS )
free ( constant . class . members );
}
free ( vm -> program . globals . members );
free ( vm -> program . constant_pool );
}
Value * alloc_constant ( VM * vm, u16 index ) {
assert ( vm -> program . constant_count > index );
Constant constant = vm -> program . constant_pool [ index ];
switch ( constant . kind ) {
case CONSTANT_INTEGER:
return make_int ( & vm -> heap, constant . integer );
case CONSTANT_NULL:
return make_null ( & vm -> heap );
case CONSTANT_BOOLEAN:
return make_bool ( & vm -> heap, constant . boolean );
Michal Štěpánek
committed
case CONSTANT_FUNCTION:
return make_cfunction ( & vm -> heap, constant . function );
default:
assert ( false );
}
return NULL;
}
ObjectValue * create_object ( VM * vm, Class * class ) {
ObjectValue * object = (ObjectValue*) make_object ( & vm -> heap, class -> member_count );
Constant * pool = vm -> program . constant_pool;
for ( size_t i = 0; i < class -> member_count; ++i ) {
Constant constant = pool [ class -> members [ i ] ];
assert ( constant . kind == CONSTANT_STRING );
object -> members [ i ] = (SimpleEntry) { .name = constant . string, .value = NULL };
}
return object;
}
void print_operand_value ( Value * value ) {
switch ( value -> kind ) {
case VALUE_INTEGER:
printf ( "%" PRIi32, ((IntValue*) value ) -> integer );
break;
case VALUE_BOOLEAN:
if ( ((BoolValue*) value ) -> boolean )
printf ( "true" );
else
printf ( "false" );
break;
case VALUE_NULL:
printf ( "null" );
break;
case VALUE_FUNCTION:
printf ( "function" );
break;
case VALUE_ARRAY:
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
putchar ( '[' );
ArrayValue * arr = (ArrayValue*) value;
for ( i32 i = 0; i < arr -> length; ++i ) {
if ( i != 0 )
printf ( ", " );
print_operand_value ( arr -> elements [ i ] );
}
putchar ( ']' );
break;
case VALUE_OBJECT:
printf ( "object(");
ObjectValue * obj = (ObjectValue*) value;
bool parent = false;
if ( obj -> extends -> kind != VALUE_NULL ) {
parent = true;
printf ( "..=" );
print_operand_value ( obj -> extends );
}
if ( obj -> member_cnt )
qsort ( obj -> members, obj -> member_cnt, sizeof (SimpleEntry), compare_entry );
for ( size_t i = 0; i < obj -> member_cnt; ++i ) {
if ( i != 0 || parent )
printf ( ", " );
printf ( "%.*s=", (int) obj -> members [ i ] . name . len, obj -> members [ i ] . name . str );
print_operand_value ( obj -> members [ i ] . value );
}
putchar ( ')' );
break;
default:
assert ( false );
}
}
void print_call ( VM * vm, u16 index, u8 arguments ) {
assert ( vm -> program . constant_count > index );
Constant constant = vm -> program . constant_pool [ index ];
assert ( constant . kind == CONSTANT_STRING );
Michal Štěpánek
committed
assert ( arguments <= vm -> stack . size );
Value ** args = (Value**) malloc ( arguments * sizeof (Value*) );
Michal Štěpánek
committed
for ( size_t i = arguments; i != 0; --i )
args [ i - 1 ] = ostack_pop ( & vm -> stack );
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
size_t arg = 0;
Str format = constant . string;
u8 c;
for ( size_t i = 0; i < format . len; ++i ) {
c = format . str [ i ];
if ( c == '\\' ) {
++i;
c = format . str [ i ];
switch ( c ) {
case '~':
putchar ( '~' );
break;
case 'n':
putchar ( '\n' );
break;
case '"':
putchar ( '"' );
break;
case 'r':
putchar ( '\r' );
break;
case 't':
putchar ( '\t' );
break;
case '\\':
putchar ( '\\' );
break;
default:
assert ( false );
}
} else if ( c == '~' ) {
assert ( arg != arguments );
print_operand_value ( args [ arg++ ] );
} else
putchar ( c );
}
free ( args );
Michal Štěpánek
committed
static inline u8 read_byte ( const u8 * data, size_t * pos ) {
return data [ (*pos)++ ];
Michal Štěpánek
committed
static inline u16 read_u16 ( const u8 * data, size_t * pos ) {
u8 bytes [ 2 ] = { read_byte ( data, pos ), read_byte ( data, pos ) };
return (uint16_t) (255 & bytes [ 1 ]) << 8 | (255 & bytes [ 0 ]);
}
Michal Štěpánek
committed
static inline i32 read_i32 ( const u8 * data, size_t * pos ) {
u8 bytes [4];
for ( size_t i = 0; i < 4; ++i )
Michal Štěpánek
committed
bytes [ i ] = read_byte ( data, pos );
return (255 & bytes [ 3 ]) << 24 | (255 & bytes [ 2 ]) << 16 | (255 & bytes [ 1 ]) << 8 | (255 & bytes [ 0 ]);
static inline Class read_class ( const u8 * data, size_t * pos ) {
Class class;
u16 elements = read_u16 ( data, pos );
class . member_count = elements;
class . members = (u16*) malloc ( elements * sizeof (u16) );
for ( size_t i = 0; i < elements; ++i )
class . members [ i ] = read_u16 ( data, pos );
return class;
}
void read_header ( Str input, size_t * pos ) {
u8 bytes [4];
Michal Štěpánek
committed
bytes [ i ] = read_byte ( input . str, pos );
Str header;
header . str = bytes;
header . len = 4;
assert ( str_eq ( header, STR ( "FML\n" ) ) );
}
void read_constant_pool ( Program * program, Str input, size_t * pos ) {
Michal Štěpánek
committed
program -> constant_count = read_u16 ( input . str, pos );
u8 tag;
Constant * constant_pool = (Constant*) malloc ( program -> constant_count * sizeof (Constant) );
for ( size_t i = 0; i < program -> constant_count; ++i ) {
Michal Štěpánek
committed
tag = read_byte ( input . str, pos );
Michal Štěpánek
committed
constant_pool [ i ] = (Constant) { .kind = CONSTANT_INTEGER, .integer = read_i32 ( input . str, pos ) };
break;
case 0x01:
constant_pool [ i ] = (Constant) { .kind = CONSTANT_NULL };
break;
case 0x02: {
Michal Štěpánek
committed
u32 len = (u32) read_i32 ( input . str, pos );
const u8 * start = input . str + *pos;
*pos += len;
constant_pool [ i ] = (Constant) { .kind = CONSTANT_STRING, .string = (Str) { .len = len, .str = start } };
break;
}
case 0x03: {
Michal Štěpánek
committed
u8 parameters = read_byte ( input . str, pos );
u16 locals = read_u16 ( input . str, pos );
u32 len = (u32) read_i32 ( input . str, pos );
const u8 * start = input . str + *pos;
*pos += len;
constant_pool [ i ] = (Constant) { .kind = CONSTANT_FUNCTION, .function = (ConstantFunction) { .parameters = parameters, .locals = locals, .len = len, .start = start } };
break;
}
case 0x04:
Michal Štěpánek
committed
constant_pool [ i ] = (Constant) { .kind = CONSTANT_BOOLEAN, .boolean = read_byte ( input . str, pos ) };
case 0x05: {
constant_pool [ i ] = (Constant) { .kind = CONSTANT_CLASS, .class = read_class ( input . str, pos ) };
}
default:
break;
}
}
program -> constant_pool = constant_pool;
}
void parse_program ( Program * program, Str input ) {
// read + check header
size_t pos = 0;
read_header ( input, &pos );
// read constant pool
read_constant_pool ( program, input, &pos );
// read globals
program -> globals = read_class ( input . str, &pos );
// read EP
Michal Štěpánek
committed
program -> entry_point = read_u16 ( input . str, &pos );
void bc_function_call ( VM * vm, u8 arguments, bool is_function, Str * name ) {
Michal Štěpánek
committed
Value * tmp [256];
arguments = is_function ? arguments : arguments - 1;
for ( size_t i = arguments; i > 0; --i )
tmp [ i - 1 ] = ostack_pop ( & vm -> stack );
Value * callee = ostack_pop ( & vm -> stack );
ConstantFunction function;
if ( is_function ) {
assert ( callee -> kind == VALUE_FUNCTION );
function = ((CFunctionValue*) callee ) -> function;
} else {
while ( callee -> kind == VALUE_OBJECT ) {
field = find_current_object_field ( callee, *name );
break;
callee = ((ObjectValue*) callee ) -> extends;
}
if ( ! field ) {
Value * result = try_operator ( & vm -> heap, callee, tmp, arguments, name );
assert ( result );
ostack_push ( & vm -> stack, result );
}
assert ( field -> kind == VALUE_FUNCTION );
function = ((CFunctionValue*) field) -> function;
}
Michal Štěpánek
committed
size_t local_count = function . parameters + function . locals;
Frame * new_frame = (Frame *) malloc ( sizeof (Frame) + local_count * sizeof (Value*) );
new_frame -> prev = vm -> local_frame;
new_frame -> return_ip = vm -> instruction_pointer;
Michal Štěpánek
committed
vm -> local_frame = new_frame;
assert ( arguments + 1 == function . parameters );
if ( is_function )
new_frame -> locals [ 0 ] = make_null ( & vm -> heap );
else
new_frame -> locals [ 0 ] = callee;
Michal Štěpánek
committed
for ( size_t i = 1; i <= arguments; ++i )
new_frame -> locals [ i ] = tmp [ i - 1 ];
for ( size_t i = arguments + 1; i < local_count; ++i )
new_frame -> locals [ i ] = make_null ( & vm -> heap );
Michal Štěpánek
committed
for ( vm -> instruction_pointer = 0; vm -> instruction_pointer < function . len; ) {
opcode = read_byte ( function . start, & vm -> instruction_pointer );
// printf ("drop\n");
Michal Štěpánek
committed
ostack_pop ( & vm -> stack );
Michal Štěpánek
committed
u16 index = read_u16 ( function . start, & vm -> instruction_pointer );
// printf ("constant %u\n", index);
Michal Štěpánek
committed
ostack_push ( & vm -> stack, alloc_constant ( vm, index ) );
// printf ("print\n");
Michal Štěpánek
committed
u16 index = read_u16 ( function . start, & vm -> instruction_pointer );
u8 arguments = read_byte ( function . start, & vm -> instruction_pointer );
Michal Štěpánek
committed
ostack_push ( & vm -> stack, make_null ( & vm -> heap) );
break;
}
case 0x03: {
// printf ("array\n");
Value * init = ostack_pop ( & vm -> stack );
Value * size = ostack_pop ( & vm -> stack );
assert ( size -> kind == VALUE_INTEGER );
size_t len = ((IntValue*) size) -> integer;
ArrayValue * array = (ArrayValue*) make_array ( & vm -> heap, len );
for ( size_t i = 0; i < len; ++i )
array -> elements [ i ] = init;
ostack_push ( & vm -> stack, (Value*) array );
break;
}
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
case 0x04: {
// printf ("object\n");
u16 index = read_u16 ( function . start, & vm -> instruction_pointer );
Constant constant = vm -> program . constant_pool [ index ];
assert ( constant . kind == CONSTANT_CLASS );
assert ( constant . class . member_count < vm -> stack . size );
ObjectValue * obj = create_object ( vm, & constant . class );
for ( size_t i = obj -> member_cnt; i > 0; --i )
obj -> members [ i - 1 ] . value = ostack_pop ( & vm -> stack );
obj -> extends = ostack_pop ( & vm -> stack );
ostack_push ( & vm -> stack, (Value*) obj );
break;
}
case 0x05: {
// printf ("get field\n");
u16 index = read_u16 ( function . start, & vm -> instruction_pointer );
assert ( index < vm -> program . constant_count );
Constant name = vm -> program . constant_pool [ index ];
assert ( name . kind == CONSTANT_STRING );
Value * object = ostack_pop ( & vm -> stack );
assert ( object -> kind == VALUE_OBJECT );
Value ** field = get_object_field ( object, name . string );
assert ( field );
ostack_push ( & vm -> stack, *field );
break;
}
case 0x06: {
// printf ("set field\n");
u16 index = read_u16 ( function . start, & vm -> instruction_pointer );
assert ( index < vm -> program . constant_count );
Constant name = vm -> program . constant_pool [ index ];
assert ( name . kind == CONSTANT_STRING );
Value * value = ostack_pop ( & vm -> stack );
Value * object = ostack_pop ( & vm -> stack );
assert ( object -> kind == VALUE_OBJECT );
Value ** field = get_object_field ( object, name . string );
assert ( field );
*field = value;
ostack_push ( & vm -> stack, value );
break;
}
case 0x07: {
// printf ("method call\n");
u16 index = read_u16 ( function . start, & vm -> instruction_pointer );
u8 arguments = read_byte ( function . start, & vm -> instruction_pointer );
assert ( index < vm -> program . constant_count );
Constant name = vm -> program . constant_pool [ index ];
assert ( name . kind == CONSTANT_STRING );
bc_function_call ( vm, arguments, false, & name . string );
break;
}
Michal Štěpánek
committed
case 0x08: {
// printf ("function call\n");
Michal Štěpánek
committed
u8 arguments = read_byte ( function . start, & vm -> instruction_pointer );
bc_function_call ( vm, arguments, true, NULL );
Michal Štěpánek
committed
break;
}
case 0x09: {
// printf ("set local\n");
Michal Štěpánek
committed
u16 index = read_u16 ( function . start, & vm -> instruction_pointer );
assert ( index < local_count );
assert ( vm -> stack . size );
new_frame -> locals [ index ] = ostack_top ( & vm -> stack );
break;
}
case 0x0A: {
// printf ("get local\n");
Michal Štěpánek
committed
u16 index = read_u16 ( function . start, & vm -> instruction_pointer );
assert ( index < local_count );
ostack_push ( & vm -> stack, new_frame -> locals [ index ] );
break;
}
case 0x0B: {
// printf ("set global\n");
u16 index = read_u16 ( function . start, & vm -> instruction_pointer );
assert ( index < vm -> program . constant_count );
Constant name = vm -> program . constant_pool [ index ];
assert ( name . kind == CONSTANT_STRING );
Value ** field = get_object_field ( vm -> globals, name . string );
assert ( field );
*field = ostack_top ( & vm -> stack );
break;
}
case 0x0C: {
// printf ("get global\n");
u16 index = read_u16 ( function . start, & vm -> instruction_pointer );
assert ( index < vm -> program . constant_count );
Constant name = vm -> program . constant_pool [ index ];
assert ( name . kind == CONSTANT_STRING );
Value ** field = get_object_field ( vm -> globals, name . string );
assert ( field );
ostack_push ( & vm -> stack, *field );
break;
}
Michal Štěpánek
committed
case 0x0D: {
Michal Štěpánek
committed
i16 offset = (i16) read_u16 ( function . start, & vm -> instruction_pointer );
assert ( (i32) vm -> instruction_pointer >= offset && vm -> instruction_pointer + offset < function . len );
Value * cond = ostack_pop ( & vm -> stack );
Michal Štěpánek
committed
if ( value_to_bool ( cond ) )
vm -> instruction_pointer += offset;
// printf (" %c\n", value_to_bool ( cond ) ? 'T' : 'F');
Michal Štěpánek
committed
case 0x0E: {
i16 offset = (i16) read_u16 ( function . start, & vm -> instruction_pointer );
// printf ("jump %d from %lu\n", offset, vm -> instruction_pointer);
assert ( (i32) vm -> instruction_pointer >= - offset && vm -> instruction_pointer + offset < function . len );
Michal Štěpánek
committed
vm -> instruction_pointer += offset;
Michal Štěpánek
committed
}
case 0x0F: {
// printf ("ret\n");
Michal Štěpánek
committed
Frame * tmp = vm -> local_frame;
vm -> local_frame = tmp -> prev;
vm -> instruction_pointer = tmp -> return_ip;
free ( tmp );
return;
}
Michal Štěpánek
committed
}
int vm_interpret ( VM * vm, Str input ) {
parse_program ( & vm -> program, input );
ostack_push ( & vm -> stack, alloc_constant ( vm, vm -> program . entry_point ) );
ObjectValue * obj = create_object ( vm, & vm -> program . globals );
for ( size_t i = 0; i < obj -> member_cnt; ++i )
obj -> members [ i ] . value = make_null ( & vm -> heap );
vm -> globals = (Value*) obj;
bc_function_call ( vm, 0, true, NULL );
Michal Štěpánek
committed