Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • vlasami6/ni-run-template
  • chybijan/ni-run
  • ruzict16/FML
  • rozhovoj/ni-run-template
  • skrabmir/cfml
  • plodeada/ni-run-plodeada
  • stepam38/ni-run
  • hrncikar/ni-run
  • balikvo1/ni-run
9 results
Show changes
Commits on Source (62)
......@@ -2,8 +2,65 @@ image: $CI_REGISTRY/vlasami6/fmltest:master
 
test:
script:
- meson setup build
- meson compile -C build
- cppcheck --error-exitcode=1 *.c
- env FML="$(readlink -f ./build/fml)" FML_REF_BC_INT=/cfml/fml /FMLtest/suite ast_interpret
hello_world.fml
- make
- export ASAN_OPTIONS=symbolize=1:debug=1:detect_leaks=1:strict_string_checks=1:detect_stack_use_after_return=1:check_initialization_order=1:strict_init_order=1
- export FML="$(readlink -f ./fml)"
- export FML_REF=/cfml/fml
- /FMLtest/suite bc_compile
hello_world
null
boolean
integer
block_result
variable_scope
variable_scope_weirdness
function_definition_0
function_definition_1
function_definition_2
function_definition_3
function_call_0
function_call_1
function_call_2
function_call_3
function_call
function_scope
function_scope_locals
function_scope_weirdness
assignment
assignment_scope
assignment_scope_weirdness
loop_0
loop_1
loop_null
loop_scope_weirdness
condition_true
condition_false
condition_int
condition_obj
condition_scope
object_fields
object_field_assignment
object_inheritence_definition
object_print
object_methods
object_operators
object_dispatch
loop_n
loop_scope
function_rec
function_value
arrays_basic
arrays_of_objects
array_comprehensions
fibonacci
fizzbuzz_fun
fizzbuzz_loop
stack
roman
brainfuck
# langtons_ant
# sudoku
- /FMLtest/suite run
brainfuck
# langtons_ant
# sudoku
CC=gcc
CFLAGS=-Wall -pedantic -Wextra -g #-fsanitize=address,undefined
LFLAGS=
OUTPUT=fml
SRC_DIR=./src
BUILD_DIR=./build
.PHONY: init fml
all: init fml
fml: $(BUILD_DIR)/arena.o $(BUILD_DIR)/parser.o $(BUILD_DIR)/ast_interpreter.o $(BUILD_DIR)/bc_interpreter.o $(BUILD_DIR)/bc_compiler.o $(SRC_DIR)/fml.c \
$(BUILD_DIR)/heap.o #$(BUILD_DIR)/%.o
$(CC) $(CFLAGS) $(LFLAGS) $? -o $(OUTPUT)
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
$(CC) $(CFLAGS) -c $< -o $@
# $(SRC_DIR)/parser.o $(BUILD_DIR)/heap.o
$(BUILD_DIR)/bc_interpreter.o: $(SRC_DIR)/bc_interpreter.c
$(CC) $(CFLAGS) -c $? -o $@
# $(SRC_DIR)/parser.o $(BUILD_DIR)/heap.o
$(BUILD_DIR)/ast_interpreter.o: $(SRC_DIR)/ast_interpreter.c
$(CC) $(CFLAGS) -c $? -o $@
$(BUILD_DIR)/parser.o: $(SRC_DIR)/parser.c
$(CC) $(CFLAGS) -c $< -o $@
$(BUILD_DIR)/arena.o: $(SRC_DIR)/arena.c
$(CC) $(CFLAGS) -c $< -o $@
$(BUILD_DIR)/heap.o: $(SRC_DIR)/heap.c
$(CC) $(CFLAGS) -c $< -o $@
init:
mkdir -p build
File moved
File moved
#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include "parser.h"
#include "ast_interpreter.h"
void env_push ( ASTInterpreterState * state ) {
Environment * env = (Environment*) malloc ( sizeof (Environment) );
(*env) = (Environment) { .prev = state -> current_env, .start = NULL };
state -> current_env = env;
}
void env_pop ( ASTInterpreterState * state ) {
Environment * tmp = state -> current_env;
EnvironmentEntry * entry;
EnvironmentEntry * next = tmp -> start;
while ( (entry = next) ) {
next = entry -> next;
free ( entry );
}
state -> current_env = tmp -> prev;
free (tmp);
}
void env_put ( ASTInterpreterState * state, Str name, Value * value ) {
Environment * env = state -> current_env;
EnvironmentEntry * entry;
while ( env ) {
entry = env -> start;
while ( entry ) {
if ( str_eq ( entry -> name, name ) ) {
entry -> value = value;
return;
}
entry = entry -> next;
}
env = env -> prev;
}
entry = state -> global_env . start;
while ( entry ) {
if ( str_eq ( entry -> name, name ) ) {
entry -> value = value;
return;
}
entry = entry -> next;
}
env = state -> current_env;
env = env ? env : & state -> global_env;
entry = (EnvironmentEntry*) malloc ( sizeof (EnvironmentEntry) );
(*entry) = (EnvironmentEntry) {.name = name, .value = value, .next = env -> start };
env -> start = entry;
}
void env_def ( ASTInterpreterState * state, Str name, Value * value ) {
Environment * env = state -> current_env ? state -> current_env : & state -> global_env;
EnvironmentEntry * parent = NULL;
EnvironmentEntry * entry = env -> start;
while ( entry ) {
if ( str_eq ( name, entry -> name ) ) {
entry -> value = value;
return;
}
parent = entry;
entry = entry -> next;
}
entry = (EnvironmentEntry*) malloc ( sizeof (EnvironmentEntry) );
entry -> name = name;
entry -> value = value;
entry -> next = NULL;
if ( parent )
parent -> next = entry;
else
env -> start = entry;
}
Value * env_get ( ASTInterpreterState * state, Str name ) {
Environment * env = state -> current_env;
EnvironmentEntry * entry;
while ( env ) {
entry = env -> start;
while ( entry ) {
if ( str_eq ( entry -> name, name ) )
return entry -> value;
entry = entry -> next;
}
env = env -> prev;
}
entry = state -> global_env . start;
while ( entry ) {
if ( str_eq ( entry -> name, name ) )
return entry -> value;
entry = entry -> next;
}
env = state -> current_env;
env = env ? env : & state -> global_env;
entry = (EnvironmentEntry*) malloc ( sizeof (EnvironmentEntry) );
(*entry) = (EnvironmentEntry) {.name = name, .value = make_null ( state -> heap ), .next = env -> start };
env -> start = entry;
return entry -> value;
}
void state_init ( ASTInterpreterState * state, Heap * heap ) {
state -> heap = heap;
state -> global_env . prev = NULL;
state -> global_env . start = NULL;
state -> current_env = NULL;
}
void state_destroy ( ASTInterpreterState * state ) {
heap_destroy ( state -> heap );
Environment * env = & state -> global_env;
EnvironmentEntry * entry;
EnvironmentEntry * next = env -> start;
while ( (entry = next) ) {
next = entry -> next;
free ( entry );
}
Environment * next_env = state -> current_env;
while ( (env = next_env) ) {
next = env -> start;
while ( (entry = next) ) {
next = entry -> next;
free ( entry );
}
next_env = env -> prev;
}
}
Value * function_call ( ASTInterpreterState * state, Value * callee, bool is_function, Ast ** arguments, size_t argc, Str * name ) {
Value ** values = (Value **) malloc ( argc * sizeof (Value*) );
for ( size_t i = 0; i < argc; ++i )
values [ i ] = evaluate ( state, arguments [ i ] );
Value * ret = NULL;
Value * function = callee;
if ( ! is_function ) {
function = NULL;
Value * next = callee;
Value * curr;
ObjectValue * tmp;
while ( next -> kind == VALUE_OBJECT ) {
curr = next;
tmp = (ObjectValue *) curr;
for ( size_t i = 0; i < tmp -> member_cnt; ++i )
if ( str_eq ( tmp -> members [ i ] . name, *name ) ) {
function = tmp -> members [ i ] . value;
callee = curr;
break;
}
next = tmp -> extends;
}
if ( ! function ) {
Value * base = get_base ( callee );
ret = try_operator ( state -> heap, base, values, argc, name );
free ( values );
if ( ret )
return ret;
fprintf ( stderr, "Method %.*s does not exist for this object and/or arguments.\n", (int) name -> len, name -> str );
exit ( 13 );
}
}
if ( function -> kind != VALUE_FUNCTION ) {
fprintf ( stderr, "Invalid callee.\n" );
exit ( 4 );
}
FunctionValue * f = (FunctionValue*) function;
Environment * tmp;
tmp = state -> current_env;
state -> current_env = NULL;
env_push ( state );
env_def ( state, STR ("this"), is_function ? make_null ( state -> heap ) : callee );
for ( size_t i = 0; i < argc; ++i )
env_def ( state, f -> function -> parameters [ i ], values [ i ] );
ret = evaluate ( state, f -> function -> body );
env_pop ( state );
state -> current_env = tmp;
free ( values );
return ret;
}
void print_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:
putchar ( '[' );
ArrayValue * arr = (ArrayValue*) value;
for ( i32 i = 0; i < arr -> length; ++i ) {
if ( i != 0 )
printf ( ", " );
print_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_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_value ( obj -> members [ i ] . value );
}
putchar ( ')' );
break;
case VALUE_INVALID:
fprintf ( stderr, "Invalid value.\n" );
}
}
void fml_print ( Str format, Value ** args, size_t argc ) {
u8 c;
size_t arg = 0;
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:
fprintf ( stderr, "Invalid escaped character %c.\n", c );
exit ( 1 );
}
} else if ( c == '~' ) {
if ( arg == argc ) {
fprintf ( stderr, "Too many placeholders, not enough arguments.\n" );
exit ( 1 );
}
print_value ( args [ arg++ ] );
} else
putchar ( c );
}
}
Value * evaluate ( ASTInterpreterState * state, Ast * ast ) {
switch ( ast -> kind ) {
case AST_INTEGER:
return make_int ( state -> heap, ((AstInteger*) ast ) -> value );
case AST_BOOLEAN:
return make_bool ( state -> heap, ((AstBoolean*) ast ) -> value );
case AST_NULL:
return make_null ( state -> heap );
case AST_PRINT: {
AstPrint * printAst = (AstPrint*) ast;
Value ** args = (Value **) malloc ( sizeof (Value*) * printAst -> argument_cnt );
for ( size_t i = 0; i < printAst -> argument_cnt; ++i )
args [ i ] = evaluate ( state, printAst -> arguments [ i ] );
fml_print ( printAst -> format, args, printAst -> argument_cnt );
free ( args );
return make_null ( state -> heap );
}
case AST_TOP: {
AstTop * topAst = (AstTop*) ast;
Value * val = evaluate ( state, topAst -> expressions [ 0 ] );
for ( size_t i = 1; i < topAst -> expression_cnt; ++i )
val = evaluate ( state, topAst -> expressions [ i ] );
return val;
}
case AST_DEFINITION: {
AstDefinition * defAst = (AstDefinition*) ast;
Value * value = evaluate ( state, defAst -> value );
env_def ( state, defAst -> name, value );
return value;
}
case AST_VARIABLE_ACCESS:
return env_get ( state, ((AstVariableAccess*) ast ) -> name );
case AST_VARIABLE_ASSIGNMENT: {
AstVariableAssignment * assignAst = (AstVariableAssignment*) ast;
Value * value = evaluate ( state, assignAst -> value );
env_put ( state, assignAst -> name, value );
return value;
}
case AST_BLOCK: {
AstBlock * blockAst = (AstBlock*) ast;
env_push ( state );
Value * value = evaluate ( state, blockAst -> expressions [ 0 ] );
for ( size_t i = 1; i < blockAst -> expression_cnt; ++i )
value = evaluate ( state, blockAst -> expressions [ i ] );
env_pop ( state );
return value;
}
case AST_CONDITIONAL: {
AstConditional * condAst = (AstConditional*) ast;
Value * cond = evaluate ( state, condAst -> condition );
env_push ( state );
Ast * cont = value_to_bool ( cond ) ? condAst -> consequent : condAst -> alternative;
Value * value = evaluate ( state, cont );
env_pop ( state );
return value;
}
case AST_LOOP: {
AstLoop * loopAst = (AstLoop*) ast;
while ( value_to_bool ( evaluate ( state, loopAst -> condition ) ) ) {
env_push ( state );
evaluate ( state, loopAst -> body );
env_pop ( state );
}
return make_null ( state -> heap );
}
case AST_FUNCTION:
return make_function ( state -> heap, (AstFunction*) ast );
case AST_FUNCTION_CALL: {
AstFunctionCall * callAst = (AstFunctionCall*) ast;
Value * function = evaluate ( state, callAst -> function );
return function_call ( state, function, true, callAst -> arguments, callAst -> argument_cnt, NULL );
}
case AST_METHOD_CALL: {
AstMethodCall * methodAst = (AstMethodCall*) ast;
Value * object = evaluate ( state, methodAst -> object );
if ( object -> kind == VALUE_FUNCTION ) {
fprintf ( stderr, "Calling method on function.\n" );
exit ( 4 );
}
return function_call ( state, object, false, methodAst -> arguments, methodAst -> argument_cnt, &methodAst -> name );
}
case AST_ARRAY: {
AstArray * arrAst = (AstArray*) ast;
Value * size = evaluate ( state, arrAst -> size );
IntValue * int_size = (IntValue*) size;
if ( size -> kind != VALUE_INTEGER || int_size -> integer < 0 ) {
fprintf ( stderr, "Invalid array size.\n" );
exit ( 5 );
}
ArrayValue * array = (ArrayValue*) make_array ( state -> heap, int_size -> integer );
for ( i32 i = 0; i < array -> length; ++i ) {
env_push ( state );
array -> elements [ i ] = evaluate ( state, arrAst -> initializer );
env_pop ( state );
}
return (Value*) array;
}
case AST_INDEX_ACCESS: {
AstIndexAccess * iaccess = (AstIndexAccess*) ast;
Value * object = evaluate ( state, iaccess -> object );
Str tmp = STR ("get");
return function_call ( state, object, false, &iaccess -> index, 1, &tmp );
}
case AST_INDEX_ASSIGNMENT: {
AstIndexAssignment * iassign = (AstIndexAssignment*) ast;
Value * object = evaluate ( state, iassign -> object );
Ast * arguments [2];
arguments [ 0 ] = iassign -> index;
arguments [ 1 ] = iassign -> value;
Str tmp = STR ("set");
return function_call ( state, object, false, arguments, 2, &tmp );
}
case AST_OBJECT: {
AstObject * objAst = (AstObject*) ast;
ObjectValue * object = (ObjectValue*) make_object ( state -> heap, objAst -> member_cnt );
object -> extends = evaluate ( state, objAst -> extends );
Value * value;
AstDefinition * defAst;
for ( size_t i = 0; i < object -> member_cnt; ++i ) {
if ( objAst -> members [ i ] -> kind != AST_DEFINITION ) {
fprintf ( stderr, "Invalid object member.\n" );
exit ( 7 );
}
defAst = (AstDefinition*) objAst -> members [ i ];
env_push ( state );
value = evaluate ( state, defAst -> value );
env_pop ( state );
object -> members [ i ] . name = defAst -> name;
object -> members [ i ] . value = value;
}
return (Value*) object;
}
case AST_FIELD_ACCESS: {
AstFieldAccess * accessAst = (AstFieldAccess *) ast;
Value * object_value = evaluate ( state, accessAst -> object );
if ( object_value -> kind != VALUE_OBJECT ) {
fprintf ( stderr, "Trying to access field of non object.\n" );
exit ( 8 );
}
Value ** result = get_object_field ( object_value, accessAst -> field );
if ( ! result ) {
fprintf ( stderr, "Field %.*s was not found in specified object.\n", (int) accessAst -> field . len, accessAst -> field . str );
exit ( 8 );
}
return *result;
}
case AST_FIELD_ASSIGNMENT: {
AstFieldAssignment * assignAst = (AstFieldAssignment*) ast;
Value * object_value = evaluate ( state, assignAst -> object );
if ( object_value -> kind != VALUE_OBJECT ) {
fprintf ( stderr, "Trying to assign to a field of non object.\n" );
exit ( 8 );
}
Value * value = evaluate ( state, assignAst -> value );
Value ** result = get_object_field ( object_value, assignAst -> field );
if ( ! result ) {
fprintf ( stderr, "Field %.*s was not found in specified object.\n", (int) assignAst -> field . len, assignAst -> field . str );
exit ( 8 );
}
*result = value;
return value;
}
}
return NULL;
}
#pragma once
#include "parser.h"
#include "heap.h"
/*typedef struct Object {
struct Value * extends;
size_t member_cnt;
SimpleEntry members [];
} Object;
typedef struct Array {
i32 length;
Value * members [];
} Array;*/
typedef struct EnvironmentEntry {
struct EnvironmentEntry * next;
Str name;
Value * value;
} EnvironmentEntry;
typedef struct Environment {
struct Environment * prev;
EnvironmentEntry * start;
} Environment;
typedef struct ASTInterpreterState {
Heap * heap;
Environment global_env;
Environment * current_env;
} ASTInterpreterState;
void env_push ( ASTInterpreterState * state );
void env_pop ( ASTInterpreterState * state );
Value * env_get ( ASTInterpreterState * state, Str name );
void env_put ( ASTInterpreterState * state, Str name, Value * value );
void env_def ( ASTInterpreterState * state, Str name, Value * value );
void state_init ( ASTInterpreterState * state, Heap * heap );
void state_destroy ( ASTInterpreterState * state );
Value * function_call ( ASTInterpreterState * state, Value * callee, bool is_function, Ast ** arguments, size_t argc, Str * name );
void print_value ( Value * value );
void fml_print ( Str format, Value ** args, size_t argc );
Value * evaluate ( ASTInterpreterState * state, Ast * ast );
\ No newline at end of file
This diff is collapsed.
#pragma once
#include "parser.h"
#include "heap.h"
#include "string.h"
#define INIT_STRING_LENGTH 256
#define MAX_SCOPE_VARIABLES 128
#define INVALID_LOCAL 65535
typedef struct String {
u32 len;
u32 capacity;
u8 * str;
} String;
typedef struct BCFunction {
u8 parameters;
u16 locals;
String bc;
} BCFunction;
typedef struct BCClass {
u16 fields;
u16 * indexes;
} BCClass;
typedef struct BCConstant {
ConstantType kind;
union {
i32 integer;
bool boolean;
Str string;
Ast * ast;
BCFunction function;
BCClass class;
};
} BCConstant;
void string_init ( String * str );
void string_destroy ( String * str );
void string_write_byte ( String * str, const u8 data );
void string_write_u16 ( String * str, const u16 data );
void string_write_i32 ( String * str, const i32 data );
void string_write_constant ( String * str, BCConstant constant );
typedef struct Constants {
BCConstant constants [ 256 * 256 ];
u16 constant_count;
i32 null_pos;
i32 bool_pos [2];
} Constants;
typedef struct Globals {
u16 names [ 256 * 256 ];
u16 global_count;
} Globals;
typedef struct LocalScope {
struct LocalScope * prev;
u16 used_locals; // locals at use in this scope
u16 local_count; // locals at use at this time
Str locals [];
} LocalScope;
typedef struct BCCompilerState {
Constants constants;
Globals globals;
LocalScope * scope;
LocalScope * top;
u16 fp;
u16 ep;
bool side_effect;
} BCCompilerState;
BCConstant create_function ( u8 parameters );
LocalScope * create_function_scope ( u8 parameters );
void add_scope ( BCCompilerState * state );
void remove_scope ( BCCompilerState * state );
void convert_function_to_bc ( BCCompilerState * state, u16 index );
u16 get_string_index ( BCCompilerState * state, Str str );
u16 get_local_index ( BCCompilerState * state, Str name );
u16 make_local_index ( BCCompilerState * state, Str name );
u16 get_or_make_class ( BCCompilerState * state, AstObject * object );
u16 get_or_make_int ( BCCompilerState * state, i32 value );
void gen_cond_body ( BCCompilerState * state, Ast * cons, Ast * alt );
void gen_loop ( BCCompilerState * state, Ast * cond, Ast * body );
void gen_loop_from_bc ( BCCompilerState * state, String * cond, String * body );
void gen_bc_array_init ( BCCompilerState * state, AstArray * array, u16 size_index );
void gen_bc_get_local ( String * body, u16 index );
void gen_bc_set_local ( String * body, u16 index );
void gen_bc_constant ( BCCompilerState * state, u16 index );
void insert_constant ( BCCompilerState * state, BCConstant constant );
void bc_state_init ( BCCompilerState * state );
void bc_state_destroy ( BCCompilerState * state );
void ast_to_bc ( BCCompilerState * state, Ast * ast );
String generate_bc ( Ast * ast );
\ No newline at end of file
#include <stdio.h>
#include <inttypes.h>
#include <assert.h>
#include <string.h>
#include "bc_interpreter.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;
}
Value * ostack_pop ( OperandStack * stack ) {
assert ( stack -> size );
stack -> size--;
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, const char * log_file ) {
heap_init ( & vm -> heap, heap_size, log_file );
heap_init ( & vm -> to, heap_size, NULL );
vm -> gc = (GarbageCollector) { .from = & vm -> heap, .to = & vm -> to, .vm = vm };
make_null ( & vm -> heap );
ostack_init ( & vm -> stack );
vm -> local_frame = NULL;
vm -> instruction_pointer = 0;
}
void vm_destroy ( VM * vm ) {
heap_destroy ( & vm -> heap );
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 );
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:
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 );
assert ( arguments <= vm -> stack . size );
Value ** args = (Value**) malloc ( arguments * sizeof (Value*) );
for ( size_t i = arguments; i != 0; --i )
args [ i - 1 ] = ostack_pop ( & vm -> stack );
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 );
assert ( arg == arguments );
}
static inline u8 read_byte ( const u8 * data, size_t * pos ) {
return data [ (*pos)++ ];
}
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 ]);
}
static inline i32 read_i32 ( const u8 * data, size_t * pos ) {
u8 bytes [4];
for ( size_t i = 0; i < 4; ++i )
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];
for ( size_t i = 0; i < 4; ++i )
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 ) {
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 ) {
tag = read_byte ( input . str, pos );
switch ( tag ) {
case 0x00:
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: {
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: {
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:
constant_pool [ i ] = (Constant) { .kind = CONSTANT_BOOLEAN, .boolean = read_byte ( input . str, pos ) };
break;
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
program -> entry_point = read_u16 ( input . str, &pos );
}
void bc_function_call ( VM * vm, u8 arguments, bool is_function, Str * name ) {
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 {
Value * field = NULL;
while ( callee -> kind == VALUE_OBJECT ) {
field = find_current_object_field ( callee, *name );
if ( field )
break;
callee = ((ObjectValue*) callee ) -> extends;
}
if ( ! field ) {
Value * result = try_operator ( & vm -> heap, callee, tmp, arguments, name );
assert ( result );
ostack_push ( & vm -> stack, result );
return;
}
assert ( field -> kind == VALUE_FUNCTION );
function = ((CFunctionValue*) field) -> function;
}
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;
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;
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 );
u8 opcode;
for ( vm -> instruction_pointer = 0; vm -> instruction_pointer < function . len; ) {
opcode = read_byte ( function . start, & vm -> instruction_pointer );
switch ( opcode ) {
case 0x00:
// printf ("drop\n");
ostack_pop ( & vm -> stack );
break;
case 0x01: {
u16 index = read_u16 ( function . start, & vm -> instruction_pointer );
// printf ("constant %u\n", index);
ostack_push ( & vm -> stack, alloc_constant ( vm, index ) );
break;
}
case 0x02: {
// printf ("print\n");
u16 index = read_u16 ( function . start, & vm -> instruction_pointer );
u8 arguments = read_byte ( function . start, & vm -> instruction_pointer );
print_call ( vm, index, arguments );
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;
}
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;
}
case 0x08: {
// printf ("function call\n");
u8 arguments = read_byte ( function . start, & vm -> instruction_pointer );
bc_function_call ( vm, arguments, true, NULL );
break;
}
case 0x09: {
// printf ("set local\n");
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");
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;
}
case 0x0D: {
// printf ("cond");
i16 offset = (i16) read_u16 ( function . start, & vm -> instruction_pointer );
if ( offset < 0 )
assert ( (i32) vm -> instruction_pointer >= offset );
else
assert ( vm -> instruction_pointer + offset < function . len );
Value * cond = ostack_pop ( & vm -> stack );
if ( value_to_bool ( cond ) )
vm -> instruction_pointer += offset;
// printf (" %c\n", value_to_bool ( cond ) ? 'T' : 'F');
break;
}
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 );
vm -> instruction_pointer += offset;
break;
}
case 0x0F: {
// printf ("ret\n");
Frame * tmp = vm -> local_frame;
vm -> local_frame = tmp -> prev;
vm -> instruction_pointer = tmp -> return_ip;
free ( tmp );
return;
}
default:
assert ( false );
}
}
free ( new_frame );
}
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 );
return 0;
}
#pragma once
#include "parser.h"
#include "heap.h"
#define OPERAND_STACK_INIT_CAPACITY 512
#define FRAME_STACK_CAPACITY 512
typedef struct Class {
u16 member_count;
u16 * members;
} Class;
typedef struct Constant {
ConstantType kind;
union {
i32 integer;
bool boolean;
Str string;
ConstantFunction function;
Class class;
};
} Constant;
typedef struct Program {
u16 constant_count;
Constant * constant_pool;
Class globals;
u16 entry_point;
} Program;
typedef struct OperandStack {
Value ** elements;
size_t size;
size_t capacity;
} OperandStack;
void ostack_init ( OperandStack * stack );
Value * ostack_pop ( OperandStack * stack );
Value * ostack_top ( OperandStack * stack );
void ostack_push ( OperandStack * stack , Value * value );
void ostack_destroy ( OperandStack * stack );
typedef struct Frame {
struct Frame * prev;
size_t return_ip;
Value * locals [];
} Frame;
typedef struct VM {
Program program;
Heap heap; // from semispace
Heap to;
OperandStack stack;
Value * globals;
Frame * local_frame;
size_t instruction_pointer;
GarbageCollector gc;
} VM;
void vm_init ( VM * vm, size_t heap_size, const char * log_file );
void vm_destroy ( VM * vm );
Value * alloc_constant ( VM * vm, u16 index );
ObjectValue * create_object ( VM * vm, Class * class );
void print_operand_value ( Value * value );
void print_call ( VM * vm, u16 index, u8 arguments );
void read_header ( Str input, size_t * pos );
void read_constant_pool ( Program * program, Str input, size_t * pos );
void parse_program ( Program * program, Str input );
void bc_function_call ( VM * vm, u8 arguments, bool is_function, Str * name );
int vm_interpret ( VM * vm, Str input );
File moved
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
#include <ctype.h>
#include "arena.h"
#include "parser.h"
#include "ast_interpreter.h"
#include "bc_interpreter.h"
#include "bc_compiler.h"
#define AST_INTERPRETER_COMMAND "ast_interpret"
#define BC_INTERPRETER_COMMAND "bc_interpret"
#define BC_COMPILER_COMMAND "bc_compile"
#define RUN "run"
#define HEAP_SIZE_COMMAND "--heap-size"
#define HEAP_LOG_COMMAND "--heap-log"
#define DEFAULT_HEAP_SIZE 100
int main ( int argc, char **argv ) {
if ( argc < 2 ) {
fprintf( stderr, "Input error: expected at least one argument\n" );
return 1;
}
size_t heap_size = DEFAULT_HEAP_SIZE;
char * log_file = NULL;
u8 flag = 0;
u8 ast_interpreter = 0;
u8 bc_interpreter = 0;
u8 bc_compiler = 0;
size_t len;
int f = -1;
for ( int arg = 1; arg < argc; ++arg ) {
len = strlen ( argv [ arg ] );
for ( size_t j = 0; j < len; j++ )
argv [ arg ] [ j ] = tolower ( argv [ arg ] [ j ] );
if ( len == strlen ( AST_INTERPRETER_COMMAND ) && strncmp ( argv [ arg ], AST_INTERPRETER_COMMAND, len ) == 0 ) {
ast_interpreter = 1;
f = ++arg;
} else if ( len == strlen ( BC_INTERPRETER_COMMAND ) && strncmp ( argv [ arg ], BC_INTERPRETER_COMMAND, len ) == 0 ) {
bc_interpreter = 1;
f = ++arg;
} else if ( len == strlen ( BC_COMPILER_COMMAND ) && strncmp ( argv [ arg ], BC_COMPILER_COMMAND, len ) == 0 ) {
bc_compiler = 1;
f = ++arg;
} else if ( len == strlen ( RUN ) && strncmp ( argv [ arg ], RUN, len ) == 0 ) {
bc_interpreter = 1;
bc_compiler = 1;
size_t remaining_args = (size_t) argc - arg;
for ( size_t opt = 0; opt < ( remaining_args > 2 ? 2 : remaining_args); ++opt ) {
len = strlen ( argv [ ++arg ] );
if ( len == strlen ( HEAP_SIZE_COMMAND ) && strncmp ( argv [ arg ], HEAP_SIZE_COMMAND, len ) == 0 ) {
heap_size = atoi ( argv [ ++arg ] );
if ( heap_size == 0 ) {
fprintf( stderr, "Input error: invalid heap size %s.\n", argv [ arg ] );
flag = 2;
}
} else if ( len == strlen ( HEAP_LOG_COMMAND ) && strncmp ( argv [ arg ], HEAP_LOG_COMMAND, len ) == 0 ) {
log_file = argv [ ++arg ];
}
}
f = ++arg;
} else {
fprintf( stderr, "Input error: unknown argument %s.\n", argv [ arg ] );
flag = 1;
}
}
if ( flag || f <= 0 )
return 1;
FILE * fp = fopen ( argv [ f ], "r" );
(void) log_file;
if ( ! fp ) {
fprintf ( stderr, "Input error: file does not exists.\n" );
return 1;
}
if ( fseek ( fp, 0, SEEK_END ) ) {
fprintf ( stderr, "Input error: could not advance to the end of the file.\n" );
return 1;
}
int file_len = ftell ( fp );
if ( file_len < 0 ) {
fprintf ( stderr, "Input error: could not count the size of the file.\n" );
return 1;
}
size_t flen = file_len;
rewind ( fp );
u8 * buffer = malloc ( flen );
if ( fread ( buffer, 1, flen, fp ) != flen ) {
fprintf ( stderr, "Input error: could not read to the file.\n" );
return 1;
}
fclose ( fp );
Str file_str = (Str) { .str = buffer, .len = flen };
if ( ast_interpreter ) {
Arena arena;
arena_init( &arena );
Ast *ast = parse_src ( &arena, file_str );
if ( ast == NULL ) {
fprintf ( stderr, "Failed to parse source\n" );
arena_destroy ( &arena );
return 1;
}
ASTInterpreterState state;
Heap heap;
heap_init ( &heap, heap_size * 1024 * 1024, log_file );
state_init ( &state, &heap );
evaluate ( &state, ast );
state_destroy ( &state );
arena_destroy( &arena );
}
String bc;
if ( bc_compiler ) {
Arena arena;
arena_init( &arena );
Ast *ast = parse_src ( &arena, file_str );
if ( ast == NULL ) {
fprintf ( stderr, "Failed to parse source\n" );
arena_destroy ( &arena );
return 1;
}
bc = generate_bc ( ast );
arena_destroy( &arena );
}
if ( bc_interpreter ) {
if ( bc_compiler )
file_str = (Str) { .len = bc . len, .str = bc . str };
VM vm;
vm_init ( &vm, heap_size * 1024 * 1024, log_file );
vm_interpret ( &vm, file_str );
vm_destroy ( &vm );
}
if ( bc_compiler ) {
if ( ! bc_interpreter )
for ( size_t i = 0; i < bc . len; ++i ) {
printf ( "%c", bc . str [ i ] );
}
string_destroy ( & bc );
}
free ( buffer );
return 0;
}
#include <stdio.h>
#include "heap.h"
#define HEADER "timestamp,event,heap\n"
void exit_handler ( GarbageCollector * gc ) {
fprintf ( stderr, "Out of memory.\n" );
exit ( 22 );
}
void gc_handler ( GarbageCollector * gc ) {
//gc -> vm;
}
void * heap_alloc_alligned ( Heap * heap, size_t len, size_t align ) {
size_t pos = (size_t) heap -> next;
size_t rem = pos % align;
if ( rem )
heap -> next = heap -> next + align - rem;
if ( heap -> next + len >= heap -> end )
return NULL;
void * ret = heap -> next;
heap -> next += len;
return ret;
}
void * heap_alloc ( Heap * heap, size_t len ) {
size_t pos = (size_t) heap -> next;
size_t rem = pos % 8;
if ( rem )
heap -> next = heap -> next + 8 - rem;
if ( heap -> next + len >= heap -> end ) {
if ( heap -> log_file )
fprintf ( heap -> log_file, "%i,%c,%lu\n", time_get (), 'B', heap -> next - heap -> begin );
heap -> full_mem_handler ( heap -> gc );
if ( heap -> log_file )
fprintf ( heap -> log_file, "%i,%c,%lu\n", time_get (), 'A', heap -> next - heap -> begin );
}
void * ret = heap -> next;
heap -> next += len;
return ret;
}
void heap_init ( Heap * heap, size_t heap_size, char * file ) {
heap -> begin = (u8*) malloc ( heap_size );
heap -> next = heap -> begin;
heap -> end = heap -> begin + heap_size;
if ( file ) {
heap -> log_file = fopen ( file, "w" );
fprintf ( file, HEADER );
fprintf ( file, "%i,%c,%lu\n", time_get (), 'S', 0 );
} else
heap -> log_file = NULL;
heap -> full_mem_handler = exit_handler;
}
void heap_destroy ( Heap * heap ) {
free ( heap -> begin );
}
Value * in_to_semispace ( GarbageCollector * gc, Value * addr ) {
if ( addr >= gc -> to -> begin && addr < gc -> to -> end )
return addr;
assert ( addr >= gc -> from -> begin && addr < gc -> from -> end );
if ( addr -> kind == VALUE_HEAP_POINTER )
return ((HeapPointerValue*) addr ) -> to;
Value * new_addr = copy_value ( gc, (Value*) addr );
*(HeapPointerValue*) addr = (HeapPointerValue) { .kind = VALUE_HEAP_POINTER, .to = new_addr };
return new_addr;
}
u8 * copy_value ( GarbageCollector * gc, Value * value ) {
switch ( value -> kind ) {
case VALUE_INTEGER:
return make_int ( gc -> to, ((IntValue *) value) -> integer);
case VALUE_BOOLEAN:
return make_bool ( gc -> to, ((BoolValue *) value) -> boolean);
case VALUE_NULL:
return make_null ( gc -> to );
case VALUE_ARRAY: {
ArrayValue * arr = (ArrayValue*) value;
ArrayValue * moved = make_array ( gc -> to, arr -> length );
for ( int i = 0; i < arr -> length; ++i )
moved -> elements [ i ] = arr -> elements [ i ];
return moved;
}
case VALUE_FUNCTION: {
CFunctionValue * func = (CFunctionValue*) value;
return make_cfunction ( gc -> to, func -> function );
}
case VALUE_OBJECT: {
ObjectValue * obj = (ObjectValue*) value;
ObjectValue * moved = make_object ( gc -> to, obj -> member_cnt );
moved -> extends = obj -> extends ;
for ( size_t i = 0; i < obj -> member_cnt; ++i )
moved -> members [ i ] = (SimpleEntry) { .name = obj -> members [ i ] . name, .value = obj -> members [ i ] . value };
return moved;
}
default:
assert (false);
}
return NULL;
}
void collect ( GarbageCollector * gc ) {
Value * value;
for ( u8 * addr = gc -> to -> begin; addr < gc -> to -> next; addr += ALIGN ) {
value = (Value*) addr;
switch ( value -> kind ) {
case VALUE_ARRAY: {
ArrayValue * arr = (ArrayValue*) value;
for ( int i = 0; i < arr -> length; ++i )
arr -> elements [ i ] = in_to_semispace ( gc, arr -> elements [ i ] );
}
case VALUE_OBJECT: {
ObjectValue * obj = (ObjectValue*) value;
obj -> extends = in_to_semispace ( gc, obj -> extends );
for ( size_t i = 0; i < obj -> member_cnt; ++i )
obj -> members [ i ] . value = in_to_semispace ( gc, obj -> members [ i ] . value );
}
case VALUE_FUNCTION:
break;
case VALUE_INTEGER:
break;
case VALUE_BOOLEAN:
break;
case VALUE_NULL:
break;
default:
fprintf ( stderr, "Tainted address in to semispace.\n" );
exit ( 21 );
}
}
}
int compare_entry ( const void * a, const void * b ) {
return str_cmp ( ((SimpleEntry*) a) -> name, ((SimpleEntry*) b) -> name );
}
bool value_to_bool ( Value * value ) {
if ( value -> kind == VALUE_NULL )
return false;
if ( value -> kind == VALUE_BOOLEAN )
return ((BoolValue*) value ) -> boolean;
return true;
}
Value * try_operator ( Heap * heap, Value * object, Value ** arguments, size_t argc, Str * name ) {
switch ( object -> kind ) {
case VALUE_INTEGER: {
if ( argc != 1 ) {
fprintf ( stderr, "Invalid argument count for integer operation %.*s.\n", (int) name -> len, name -> str );
exit ( 10 );
}
IntValue * this = (IntValue*) object;
bool is_int = arguments [ 0 ] -> kind == VALUE_INTEGER;
if ( is_int ) {
IntValue * other = (IntValue*) arguments [ 0 ];
if ( str_eq ( *name, STR ("+") ) )
return make_int ( heap, this -> integer + other -> integer );
if ( str_eq ( *name, STR ("-") ) )
return make_int ( heap, this -> integer - other -> integer );
if ( str_eq ( *name, STR ("*") ) )
return make_int ( heap, this -> integer * other -> integer );
if ( str_eq ( *name, STR ("/") ) )
return make_int ( heap, this -> integer / other -> integer );
if ( str_eq ( *name, STR ("%") ) )
return make_int ( heap, this -> integer % other -> integer );
if ( str_eq ( *name, STR ("<=") ) )
return make_bool ( heap, this -> integer <= other -> integer );
if ( str_eq ( *name, STR (">=") ) )
return make_bool ( heap, this -> integer >= other -> integer );
if ( str_eq ( *name, STR ("<") ) )
return make_bool ( heap, this -> integer < other -> integer );
if ( str_eq ( *name, STR (">") ) )
return make_bool ( heap, this -> integer > other -> integer );
if ( str_eq ( *name, STR ("==") ) )
return make_bool ( heap, this -> integer == other -> integer );
if ( str_eq ( *name, STR ("!=") ) )
return make_bool ( heap, this -> integer != other -> integer );
break;
}
if ( str_eq ( *name, STR ("==") ) )
return make_bool ( heap, false );
if ( str_eq ( *name, STR ("!=") ) )
return make_bool ( heap, true );
break;
}
case VALUE_BOOLEAN: {
if ( argc != 1 ) {
fprintf ( stderr, "Invalid argument count for bool operation %.*s.\n", (int) name -> len, name -> str );
exit ( 10 );
}
BoolValue * this = (BoolValue*) object;
bool is_bool = arguments [ 0 ] -> kind == VALUE_BOOLEAN;
if ( is_bool ) {
BoolValue * other = (BoolValue*) arguments [ 0 ];
if ( str_eq ( *name, STR ("&") ) )
return make_bool ( heap, this -> boolean & other -> boolean );
if ( str_eq ( *name, STR ("|") ) )
return make_bool ( heap, this -> boolean | other -> boolean );
if ( str_eq ( *name, STR ("==") ) )
return make_bool ( heap, this -> boolean == other -> boolean );
if ( str_eq ( *name, STR ("!=") ) )
return make_bool ( heap, this -> boolean != other -> boolean );
break;
}
if ( str_eq ( *name, STR ("==") ) )
return make_bool ( heap, false );
if ( str_eq ( *name, STR ("!=") ) )
return make_bool ( heap, true );
break;
}
case VALUE_NULL: {
if ( argc != 1 ) {
fprintf ( stderr, "Invalid amount of arguments for null operation %.*s.\n", (int) name -> len, name -> str );
exit ( 10 );
}
if ( str_eq ( *name, STR ("==") ) )
return make_bool ( heap, arguments [ 0 ] -> kind == VALUE_NULL ? true : false );
if ( str_eq ( *name, STR ("!=") ) )
return make_bool ( heap, arguments [ 0 ] -> kind != VALUE_NULL ? true : false );
break;
}
case VALUE_ARRAY:
if ( str_eq ( *name, STR ("get") ) ) {
if ( argc != 1 || arguments [ 0 ] -> kind != VALUE_INTEGER ) {
fprintf ( stderr, "Invalid argument for array get.\n" );
exit ( 11 );
}
ArrayValue * array = (ArrayValue*) object;
i32 index = ((IntValue*) arguments [ 0 ]) -> integer;
if ( index < 0 || index >= array -> length ) {
fprintf ( stderr, "Index is out of bounds.\n" );
exit ( 11 );
}
return array -> elements [ index ];
}
if ( str_eq ( *name, STR ("set") ) ) {
if ( argc != 2 || arguments [ 0 ] -> kind != VALUE_INTEGER ) {
fprintf ( stderr, "Invalid arguments for array set.\n" );
exit ( 12 );
}
ArrayValue * array = (ArrayValue*) object;
i32 index = ((IntValue*) arguments [ 0 ]) -> integer;
if ( index < 0 || index >= array -> length ) {
fprintf ( stderr, "Index is out of bounds.\n" );
exit ( 12 );
}
array -> elements [ index ] = arguments [ 1 ];
return arguments [ 1 ];
}
break;
default:
break;
}
return NULL;
}
Value * get_base ( Value * object ) {
Value * curr = object;
while ( curr -> kind == VALUE_OBJECT )
curr = ((ObjectValue *) curr) -> extends;
return curr;
}
Value ** get_object_field ( Value * object, Str name ) {
Value * next = object;
ObjectValue * curr;
do {
curr = (ObjectValue*) next;
for ( size_t i = 0; i < curr -> member_cnt; ++i )
if ( str_eq ( curr -> members [ i ] . name, name ) )
return & curr -> members [ i ] . value;
next = curr -> extends;
} while ( next -> kind == VALUE_OBJECT );
return NULL;
}
Value * find_current_object_field ( Value * object, Str name ) {
ObjectValue * curr = (ObjectValue*) object;
for ( size_t i = 0; i < curr -> member_cnt; ++i )
if ( str_eq ( curr -> members [ i ] . name, name ) )
return curr -> members [ i ] . value;
return NULL;
}
Value * make_int ( Heap * heap, i32 val ) {
IntValue * value = heap_alloc ( heap, sizeof (IntValue) );
*value = (IntValue) { .kind = (Value) { .kind = VALUE_INTEGER }, .integer = val };
return (Value*) value;
}
Value * make_bool ( Heap * heap, bool val ) {
BoolValue * value = heap_alloc ( heap, sizeof (BoolValue) );
*value = (BoolValue) { .kind = (Value) { .kind = VALUE_BOOLEAN }, .boolean = val };
return (Value*) value;
}
Value * make_function ( Heap * heap, AstFunction * function ) {
FunctionValue * value = heap_alloc ( heap, sizeof (FunctionValue) );
*value = (FunctionValue) { .kind = (Value) { .kind = VALUE_FUNCTION }, .function = function };
return (Value*) value;
}
Value * make_cfunction ( Heap * heap, ConstantFunction function ) {
CFunctionValue * value = heap_alloc ( heap, sizeof (CFunctionValue) );
*value = (CFunctionValue) { .kind = (Value) { .kind = VALUE_FUNCTION }, .function = function };
return (Value*) value;
}
Value * make_array ( Heap * heap, size_t len ) {
ArrayValue * value = heap_alloc ( heap, sizeof (ArrayValue) + len * sizeof (Value*) );
*value = (ArrayValue) { .kind = (Value) { .kind = VALUE_ARRAY }, .length = len };
return (Value*) value;
}
Value * make_object ( Heap * heap, size_t member_cnt ) {
ObjectValue * value = heap_alloc ( heap, sizeof (ObjectValue) + member_cnt * sizeof (SimpleEntry) );
*value = (ObjectValue) { .kind = (Value) { .kind = VALUE_OBJECT }, .member_cnt = member_cnt };
return (Value*) value;
}
Value * make_null ( Heap * heap ) {
NullValue * value = heap_alloc ( heap, sizeof (NullValue) );
*value = (NullValue) { .kind = (Value) { .kind = VALUE_NULL } };
return (Value*) value;
}
#pragma once
#include "parser.h"
#include "bc_interpreter.h"
#include <stdio.h>
#define ALIGN 8
typedef enum {
VALUE_INTEGER,
VALUE_BOOLEAN,
VALUE_NULL,
VALUE_FUNCTION,
VALUE_ARRAY,
VALUE_OBJECT,
VALUE_INVALID,
VALUE_HEAP_POINTER
} ValueType;
typedef enum {
CONSTANT_INTEGER,
CONSTANT_BOOLEAN,
CONSTANT_NULL,
CONSTANT_STRING,
CONSTANT_AST_FUNCTION,
CONSTANT_FUNCTION,
CONSTANT_CLASS,
} ConstantType;
struct GarbageCollector;
typedef struct Heap {
u8 * begin;
u8 * next;
u8 * end;
FILE * log_file;
struct GarbageCollector * gc;
void (*full_mem_handler)(struct GarbageCollector*);
} Heap;
typedef struct Value {
ValueType kind;
} Value;
typedef struct SimpleEntry {
Str name;
Value * value;
} SimpleEntry;
typedef struct NullValue {
Value kind;
} NullValue;
typedef struct IntValue {
Value kind;
i32 integer;
} IntValue;
typedef struct BoolValue {
Value kind;
bool boolean;
} BoolValue;
typedef struct FunctionValue {
Value kind;
AstFunction * function;
} FunctionValue;
typedef struct ConstantFunction {
u32 len;
const u8 * start;
u16 locals;
u8 parameters;
} ConstantFunction;
typedef struct CFunctionValue {
Value kind;
ConstantFunction function;
} CFunctionValue;
typedef struct ObjectValue {
Value kind;
Value * extends;
size_t member_cnt;
SimpleEntry members [];
} ObjectValue;
typedef struct ArrayValue {
Value kind;
i32 length;
Value * elements [];
} ArrayValue;
typedef struct HeapPointerValue {
Value kind;
u8 * to;
} HeapPointerValue;
typedef struct GarbageCollector {
Heap * from;
Heap * to;
VM * vm;
} GarbageCollector;
void exit_handler ( GarbageCollector * gc );
void gc_handler ( GarbageCollector * gc );
void * heap_alloc_alligned ( Heap * heap, size_t len, size_t align );
void * heap_alloc ( Heap * heap, size_t len );
void heap_init ( Heap * heap, size_t heap_size, char * file );
void heap_destroy ( Heap * heap );
// return to address of value at from semispace
Value * in_to_semispace ( GarbageCollector * gc, Value * addr );
// adds Value into to semispace
u8 * copy_value ( GarbageCollector * gc, Value * value );
// function assumes that to containts moved roots and that in their position in from semispace are valid forwarding pointers
// also alignment to 8B is assumed
void collect ( GarbageCollector * gc );
int compare_entry ( const void * a, const void * b );
bool value_to_bool ( Value * value );
Value * try_operator ( Heap * heap, Value * object, Value ** arguments, size_t argc, Str * name );
Value * get_base ( Value * object );
Value ** get_object_field ( Value * object, Str name );
Value * find_current_object_field ( Value * object, Str name );
Value * make_null ( Heap * heap );
Value * make_int ( Heap * heap, i32 val );
Value * make_bool ( Heap * heap, bool val );
Value * make_function ( Heap * heap, AstFunction * function );
Value * make_cfunction ( Heap * heap, ConstantFunction function );
Value * make_array ( Heap * heap, size_t len );
Value * make_object ( Heap * heap, size_t member_cnt );
File moved
File moved