diff --git a/lang/compile.py b/lang/compile.py index 6166b8f..2444958 100755 --- a/lang/compile.py +++ b/lang/compile.py @@ -125,12 +125,21 @@ return 'ASTFunction(public=%s, class="%s", name="%s", args="%s", statements=%s)' % \ (self.public, self.class_, self.name, self.args, self.statements) -class ASTClass(): - def __init__(self, name): +class ASTClassField(): + def __init__(self, public, name): + self.public = public self.name = name def __repr__(self): - return 'ASTClass(name=%s)' % (self.name) + return 'ASTClassField(public=%s, name=%s)' % (self.public, self.name) + +class ASTClass(): + def __init__(self, name, fields): + self.name = name + self.fields = fields + + def __repr__(self): + return 'ASTClass(name=%s, fields=%s)' % (self.name, self.fields) ## Parser @@ -361,6 +370,17 @@ metadata = ASTMetadata(id, module, use_list) return (metadata, lines) +def parse_class_field(lines): + line = lines[0] + lines = lines[1:] + if len(line) != 2 or line[0] not in ["Public", "Private"]: + print("not a class field? line: %s" % (' '.join(line))) + return (None, []) + name = line[1] + public = line[0] == "Public" + field = ASTClassField(public, name) + return (field, lines) + def parse_class(lines): line = lines[0] lines = lines[1:] @@ -374,8 +394,12 @@ if first_word == "EndClass": lines = lines[1:] break - return (None, []) - class_ = ASTClass(name) + (field, lines) = parse_class_field(lines) + if field is None: + return (None, []) + else: + fields.append(field) + class_ = ASTClass(name, fields) return (class_, lines) def parse_toplevel(lines, id): @@ -541,13 +565,22 @@ def __repr__(self): return 'IRMetadata(id=%s, name=%s, uses=%s, classes=%s)' % (self.id, self.name, self.uses, self.classes_) -class IRClass(): - def __init__(self, name, functions): +class IRClassField(): + def __init__(self, public, name): + self.public = public self.name = name - self.functions = functions def __repr__(self): - return 'IRClass(name=%s, functions=%s)' % (self.name, self.functions) + return 'IRClassField(public=%s, name=%s)' % (self.public, self.name) + +class IRClass(): + def __init__(self, name, functions, fields): + self.name = name + self.functions = functions + self.fields = fields + + def __repr__(self): + return 'IRClass(name=%s, functions=%s, fields=%s)' % (self.name, self.functions, self.fields) ## IR Generator @@ -696,9 +729,18 @@ def generate_ir_metadata(ast): return IRMetadata(ast.id, ast.name, ast.uses, []) +def generate_ir_class_field(ast): + name = ast.name + public = ast.public + return IRClassField(public, name) + def generate_ir_class(ast): name = ast.name - return IRClass(name, []) + fields = [] + for field in ast.fields: + field_ir = generate_ir_class_field(field) + fields.append(field_ir) + return IRClass(name, [], fields) def generate_ir(ast): ir = [] @@ -741,7 +783,7 @@ if metadata is None: print("Unable to find module metadata?") return None - return IRClass(metadata.name, []) + return IRClass(metadata.name, [], []) def map_class_functions(functions, classes): mapped_classes = [] @@ -755,7 +797,8 @@ print("Unknown class %s" % (name)) return None funcs = functions[name] - new_class = IRClass(name, funcs) + fields = class_.fields + new_class = IRClass(name, funcs, fields) mapped_classes.append(new_class) return mapped_classes @@ -1088,7 +1131,12 @@ % (node.class_, node.name, c_bytecode) def generate_class_call(class_name, func_name, index): - call = '{.name = "%s", .bytecode_index = %s },\n\t' % (func_name, index) + call = '{.name = "%s", .op = OP_BYTECODE, .index = %s },\n\t' % (func_name, index) + return call + +def generate_class_field(field, index): + call = '{.name = "%s", .op = OP_FIELDGET, .index = %s },\n\t' % (field, index) + call += '{.name = "Set%s", .op = OP_FIELDSET, .index = %s },\n\t' % (field, index) return call def generate_class_structs(class_ir): @@ -1099,6 +1147,11 @@ calls += generate_class_call(class_ir.name, func, index) bytecodes += "bytecode_%s_%s,\n\t" % (class_ir.name, func) index += 1 + index = 0 + for field in class_ir.fields: + calls += generate_class_field(field.name, index) + index += 1 + fields_count = len(class_ir.fields) return """static const unsigned char *CLASSNAME_bytecodes[] = { %sNULL, }; @@ -1108,10 +1161,10 @@ }; static const ModuleClass CLASSNAME_module_class = { - .fields_count = 0, + .fields_count = %i, .bytecodes = CLASSNAME_bytecodes, .calls = CLASSNAME_module_calls, -};""" % (bytecodes, calls) +};""" % (bytecodes, calls, fields_count) def generate_c_class(class_ir): functions = [] diff --git a/lang/list.c b/lang/list.c index e8360fa..887e9c6 100644 --- a/lang/list.c +++ b/lang/list.c @@ -59,6 +59,16 @@ return new; } +Object object_list_copy(VmState state, Object obj) { + int length = object_list_length(state, obj); + Object new = object_list_create(state, length); + for (int i = 0; i < length; ++i) { + Object elem = object_list_get(state, obj, i); + object_list_set(state, new, i, elem); + } + return new; +} + static struct object_call object_list_calls[] = {{.name = NULL, /* end */}}; static struct object_class object_list_class = { diff --git a/lang/list.h b/lang/list.h index 1f452b7..e020e90 100644 --- a/lang/list.h +++ b/lang/list.h @@ -21,4 +21,6 @@ // A new reference is created for the caller Object object_list_get(VmState state, Object list, int index); +Object object_list_copy(VmState state, Object list); + #endif diff --git a/lang/main.c b/lang/main.c index a42c71d..9081450 100644 --- a/lang/main.c +++ b/lang/main.c @@ -9,6 +9,17 @@ #include "vm.h" #include +static void test_class(VmState state) { + Object module = module_find(state, "Main"); + modules_free(state); + vm_stack_push(state, object_none()); + vm_call(state, module, "TwoNumbersTest", 1); + Object ret = vm_stack_pop(state); + vm_abort_if(state, !boolean_value(state, ret), "class test failed"); + object_drop(state, &ret); + object_drop(state, &module); +} + static void test_rational(VmState state) { Object ratioA = rational_create(state, 5, 1); Object ratioB = rational_create(state, 3, 1); @@ -61,6 +72,7 @@ int lang_main(void) { VmState state = vm_create(); + test_class(state); test_rational(state); test_module(state); vm_destroy(&state); diff --git a/lang/module.c b/lang/module.c index e06e3a5..29b2711 100644 --- a/lang/module.c +++ b/lang/module.c @@ -91,6 +91,7 @@ struct module_class_data { const ModuleClass *class; Object module_runtime; + Object fields; }; Object module_class_create(VmState state, Object module_runtime, int index) { @@ -100,15 +101,62 @@ (struct module_class_data *)object_priv( state, obj, &module_class_class); data->class = module_runtime_class_info(state, module_runtime, index); + data->fields = object_list_create(state, data->class->fields_count); data->module_runtime = module_runtime; return obj; } +Object module_class_copywrite( + VmState state, Object old, int index, Object value) { + struct module_class_data *old_data = + (struct module_class_data *)object_priv( + state, old, &module_class_class); + size_t size = sizeof(struct module_class_data); + Object obj = object_create(state, &module_class_class, size); + struct module_class_data *data = + (struct module_class_data *)object_priv( + state, obj, &module_class_class); + // Create a new reference for the module runtime + object_hold(state, old_data->module_runtime); + data->class = old_data->class; + data->module_runtime = old_data->module_runtime; + data->fields = object_list_copy(state, old_data->fields); + object_list_set(state, data->fields, index, value); + return obj; +} + static void module_class_cleanup(VmState state, Object obj) { struct module_class_data *data = (struct module_class_data *)object_priv( state, obj, &module_class_class); object_drop(state, &data->module_runtime); + object_drop(state, &data->fields); +} + +void module_class_dispatch(VmState state, Object obj, + struct module_class_data *data, const ModuleClassCall *call) { + int op = call->op; + int index = call->index; + if (op == OP_BYTECODE) { + const unsigned char *bytecode = data->class->bytecodes[index]; + bytecode_run(state, obj, bytecode, data->module_runtime); + } else if (op == OP_FIELDGET) { + int arg_count = vm_stack_depth(state); + vm_abort_if( + state, arg_count != 1, "getter called with arguments"); + Object field = object_list_get(state, data->fields, index); + vm_stack_set(state, 0, field); + } else if (op == OP_FIELDSET) { + int arg_count = vm_stack_depth(state); + vm_abort_if(state, arg_count != 2, + "setter called without an argument"); + Object new_value = vm_stack_pop(state); + Object new_obj = + module_class_copywrite(state, obj, index, new_value); + vm_stack_set(state, 0, new_obj); + } else { + vm_abort_msg(state, "module_class_dispatch: unknown operation"); + } } void module_class_dispatcher(VmState state, Object obj, const char *name) { @@ -118,10 +166,7 @@ const ModuleClassCall *call = data->class->calls; while (call->name != NULL) { if (strcmp(call->name, name) == 0) { - const unsigned char *bytecode = - data->class->bytecodes[call->bytecode_index]; - bytecode_run( - state, obj, bytecode, data->module_runtime); + module_class_dispatch(state, obj, data, call); return; } ++call; diff --git a/lang/module.h b/lang/module.h index 5d4df42..b7ca27c 100644 --- a/lang/module.h +++ b/lang/module.h @@ -20,10 +20,16 @@ // the compiler's C code generator. Do not use for regular C code. #ifdef MODULE_INTERNAL_API +// Operations for each class call +#define OP_BYTECODE 0 +#define OP_FIELDGET 1 +#define OP_FIELDSET 2 + // A call the module class can make struct module_class_call { const char *name; - const int bytecode_index; + const int op; + const int index; }; typedef struct module_class_call ModuleClassCall; diff --git a/lang/modules/main.txt b/lang/modules/main.txt index c2ed0d7..bc32ccf 100644 --- a/lang/modules/main.txt +++ b/lang/modules/main.txt @@ -1,6 +1,37 @@ Module Main Use Another +Note A class containing two values: First, Second +Class TwoNumbers + Public First + Public Second + Private SecretValue +EndClass + +Note This function creates a new TwoNumbers object and checks that +Note its fields have correctly stored the value +Public Class Main Function TwoNumbersTest + Note Create numbers + Set FirstNum To 1234 + Set SecondNum To 456 + Note Create object and get fields + Set Object0 To Create TwoNumbers + Set Object1 To Object0 SetFirst FirstNum + Set Object2 To Object1 SetSecond SecondNum + Note Tests copy on write + Set Object3 To Object2 SetFirst 0 + Note Get the fields + Set ObjFirst To Object2 First + Set ObjSecond To Object2 Second + Note Calculate if fields match + Set FirstMatches To FirstNum Equals ObjFirst + Set SecondMatches To SecondNum Equals ObjSecond + Note Check that all of them match + Set AllMatch To FirstMatches And SecondMatches + Note Return whether they all match + Return AllMatch +EndFunction + Public Class Main Function BoolTest Set A To True Set B To False @@ -49,17 +80,9 @@ Jump Self CountDown 10000 EndFunction -Class MakeNumberClass -EndClass - -Public Class MakeNumberClass Function DoIt +Public Class Main Function MakeNumber Set Halfish To Another GetHalfish 620 Set Double To Halfish Add Halfish Set Final To Double Subtract 2 Return Final EndFunction - -Public Class Main Function MakeNumber - Set Maker To Create MakeNumberClass - Jump Maker DoIt -EndFunction