diff --git a/altlib/test_allocator.al b/altlib/test_allocator.al index 9a732a6..1331ae8 100644 --- a/altlib/test_allocator.al +++ b/altlib/test_allocator.al @@ -1,5 +1,10 @@ include altlib."posix.al" +def PROT_READ := 1 +def PROT_WRITE := 2 +def MAP_PRIVATE := 2 +def MAP_ANONYMOUS := 32 + typedef TestAllocator := #S { data: #1I, size: #I, @@ -11,10 +16,6 @@ typedef TestAllocator := #S { //* #1TestAllocator.init //* Initializes an allocator with size `size` and allocates a buffer. func ^#1TestAllocator.init(this #1TestAllocator, size #I) -> #V { - def PROT_READ := 1 - def PROT_WRITE := 2 - def MAP_PRIVATE := 2 - def MAP_ANONYMOUS := 32 this->data& <- posix_mmap(0 as #1I, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) this->size& <- 0 this->reserved& <- size diff --git a/compiler/include/ast.h b/compiler/include/ast.h index fe5c252..ed8edb2 100644 --- a/compiler/include/ast.h +++ b/compiler/include/ast.h @@ -5,6 +5,7 @@ #include enum NodeType { + NodeModule, NodeBlock, NodeInclude, NodeTest, @@ -12,6 +13,7 @@ enum NodeType { NodeWhile, NodeFunctionDefinition, NodePrototype, + NodeGlobalDefinition, NodeDefinition, NodeTypeDefinition, NodeReturn, @@ -57,6 +59,7 @@ enum NodeType { }; struct Node; +struct Module; struct Block; struct Include; struct Test; @@ -64,6 +67,7 @@ struct If; struct While; struct FunctionDefinition; struct Prototype; +struct GlobalDefinition; struct Definition; struct TypeDefinition; struct Return; @@ -94,6 +98,7 @@ struct FunctionSignature { struct Vector identifiers; struct Vector types; struct TypeNode *return_type; + bool propagate_allocator; }; struct Node { @@ -103,6 +108,10 @@ struct Node { enum NodeType node_type; }; +struct Module { + struct Vector statement_list; +}; + struct Block { struct Vector statement_list; const char *label; @@ -144,6 +153,12 @@ struct Prototype { struct FunctionSignature *signature; }; +struct GlobalDefinition { + const char *identifier; + struct TypeNode *type; + struct Node *value; +}; + struct Definition { const char *identifier; struct TypeNode *type; @@ -220,6 +235,7 @@ struct Sizeof { struct FunctionCall { struct Node *function; struct Vector arguments; + struct Node *propagate_allocator; }; struct MethodCall { diff --git a/compiler/include/context.h b/compiler/include/context.h index e888be5..597ce00 100644 --- a/compiler/include/context.h +++ b/compiler/include/context.h @@ -4,6 +4,11 @@ #include #include +struct GlobalVariableInfo { + const char *name; + struct TypeNode *type; +}; + struct VariableInfo { const char *name; struct TypeNode *type; @@ -32,6 +37,7 @@ struct LabelInfo { struct CPContext { struct Vector variables; + struct Vector global_variables; struct Vector types; struct Vector functions; struct Vector block_labels; @@ -55,11 +61,13 @@ struct CPContext { struct TypeNode *node_void; struct TypeNode *node_int; struct TypeNode *node_char; + struct TypeNode *node_allocator; }; -struct VariableInfo *context_find_variable (struct CPContext*, const char*); -struct TypeInfo *context_find_type (struct CPContext*, const char*); -struct FunctionInfo *context_find_function (struct CPContext*, const char*); -struct FunctionInfo *context_find_method (struct CPContext*, const char*, struct TypeNode*); -struct LabelInfo *context_find_block_label (struct CPContext*, const char*); -struct LabelInfo *context_find_loop_label (struct CPContext*, const char*); +struct GlobalVariableInfo *context_find_global_variable(struct CPContext*, const char*); +struct VariableInfo *context_find_variable (struct CPContext*, const char*); +struct TypeInfo *context_find_type (struct CPContext*, const char*); +struct FunctionInfo *context_find_function (struct CPContext*, const char*); +struct FunctionInfo *context_find_method (struct CPContext*, const char*, struct TypeNode*); +struct LabelInfo *context_find_block_label (struct CPContext*, const char*); +struct LabelInfo *context_find_loop_label (struct CPContext*, const char*); diff --git a/compiler/include/token.h b/compiler/include/token.h index 4508296..a562c67 100644 --- a/compiler/include/token.h +++ b/compiler/include/token.h @@ -45,6 +45,7 @@ enum TokenType { TokenDereference, TokenGetField, TokenSharp, + TokenAt, TokenPlus, TokenMinus, TokenMult, diff --git a/compiler/include/typeast.h b/compiler/include/typeast.h index 63f5120..2e9b05f 100644 --- a/compiler/include/typeast.h +++ b/compiler/include/typeast.h @@ -1,5 +1,6 @@ #pragma once +#include #include enum TypeNodeType { @@ -46,6 +47,7 @@ struct TypeStruct { struct TypeFunction { struct Vector types; struct TypeNode *return_type; + bool propagate_allocator; }; struct TypeIdentifier { diff --git a/compiler/src/compile.c b/compiler/src/compile.c index eb6e483..c44bbe9 100644 --- a/compiler/src/compile.c +++ b/compiler/src/compile.c @@ -67,6 +67,7 @@ void generate_test_function(struct CPContext *context, struct Settings *settings void compile_process(struct Node *node, struct Settings *settings) { struct CPContext *context = (struct CPContext*)_malloc(sizeof(struct CPContext)); context->variables = vnew(); + context->global_variables = vnew(); context->types = vnew(); context->functions = vnew(); context->block_labels = vnew(); @@ -101,6 +102,14 @@ void compile_process(struct Node *node, struct Settings *settings) { context->node_char->node_type = TypeNodeChar; context->node_char->degree = 0; } + { + context->node_allocator = (struct TypeNode*)_malloc(sizeof(struct TypeNode)); + struct TypeIdentifier *_type = (struct TypeIdentifier*)_malloc(sizeof(struct TypeIdentifier)); + context->node_allocator->node_ptr = _type; + context->node_allocator->node_type = TypeNodeIdentifier; + context->node_allocator->degree = 1; + _type->identifier = _strdup("TestAllocator"); + } int fd[2]; @@ -154,6 +163,24 @@ void compile_process(struct Node *node, struct Settings *settings) { } } +void compile_module(struct Node *node, struct Module *this, struct CPContext *context) { + int old_cnt_var = vsize(&context->variables); + int old_cnt_functions = vsize(&context->functions); + + for (int i = 0; i < vsize(&this->statement_list); i++) { + compile_node(this->statement_list.ptr[i], context); + } + + int cnt_var = vsize(&context->variables); + int cnt_functions = vsize(&context->functions); + for (int i = 0; i < cnt_var - old_cnt_var; i++) { + vpop(&context->variables); + } + for (int i = 0; i < cnt_functions - old_cnt_functions; i++) { + vpop(&context->functions); + } +} + struct TypeNode *compile_block(struct Node *node, struct Block *this, struct CPContext *context) { struct LabelInfo *label_info = (struct LabelInfo*)_malloc(sizeof(struct LabelInfo)); if(this->label) label_info->name = this->label; @@ -229,11 +256,25 @@ struct TypeNode *compile_function_signature(struct Node *node, struct FunctionSi } } + sz += (this->propagate_allocator != NULL); + for (int i = 0; i < sz; i++) { _fputs3(context->fd_text, "push ", regs[i], "\n"); struct VariableInfo *var_info = (struct VariableInfo*)_malloc(sizeof(struct VariableInfo)); - var_info->name = this->identifiers.ptr[i]; - var_info->type = this->types.ptr[i]; + if (this->propagate_allocator) { + if (i == 0) { + var_info->name = "@"; + var_info->type = context->node_allocator; + } + else { + var_info->name = this->identifiers.ptr[i - 1]; + var_info->type = this->types.ptr[i - 1]; + } + } + else { + var_info->name = this->identifiers.ptr[i]; + var_info->type = this->types.ptr[i]; + } int size = align_to_word(type_size(var_info->type, context)); var_info->sf_phase = context->sf_pos - size; context->sf_pos -= size; @@ -356,18 +397,22 @@ struct TypeNode *compile_while(struct Node *node, struct While *this, struct CPC return type; } -struct TypeNode *from_signature_to_type(struct FunctionSignature *signature) { +struct TypeNode *from_signature_to_type(struct FunctionSignature *signature, struct CPContext *context) { struct TypeNode *type = (struct TypeNode*)_malloc(sizeof(struct TypeNode)); struct TypeFunction *_type = (struct TypeFunction*)_malloc(sizeof(struct TypeFunction)); type->node_ptr = _type; type->node_type = TypeNodeFunction; type->degree = 0; _type->types = vnew(); + if (signature->propagate_allocator) { + vpush(&_type->types, context->node_allocator); + } int sz = vsize(&signature->types); for (int i = 0; i < sz; i++) { vpush(&_type->types, signature->types.ptr[i]); } _type->return_type = signature->return_type; + _type->propagate_allocator = (signature->propagate_allocator != NULL); return type; } @@ -398,7 +443,7 @@ void compile_function_definition(struct Node *node, struct FunctionDefinition *t function_info->name_front = identifier_front; function_info->name_back = identifier_back; function_info->caller_type = this->caller_type; - function_info->type = from_signature_to_type(this->signature); + function_info->type = from_signature_to_type(this->signature, context); vpush(&context->functions, function_info); if (this->external) { @@ -445,7 +490,7 @@ void compile_prototype(struct Node *node, struct Prototype *this, struct CPConte struct FunctionInfo *function_info = (struct FunctionInfo*)_malloc(sizeof(struct FunctionInfo)); function_info->name_front = identifier; function_info->name_back = identifier; - function_info->type = from_signature_to_type(this->signature); + function_info->type = from_signature_to_type(this->signature, context); function_info->caller_type = this->caller_type; vpush(&context->functions, function_info); @@ -461,15 +506,50 @@ void compile_prototype(struct Node *node, struct Prototype *this, struct CPConte } } +void compile_global_definition(struct Node *node, struct GlobalDefinition *this, struct CPContext *context) { + struct TypeNode *_type; + if (this->value) { + _type = context->node_int; + if (this->value->node_type != NodeInteger) { + error_semantic("Const integer expected as initial value in global variable", node); + } + if (this->type && !type_equal(_type, this->type, context)) { + error_semantic("Declared and inferred types are not equal", node); + } + } + else { + if (this->type) { + _type = this->type; + } + else { + error_semantic("Undefined type in definition", node); + } + } + + struct GlobalVariableInfo *global_var_info = (struct GlobalVariableInfo*)_malloc(sizeof(struct GlobalVariableInfo)); + global_var_info->name = _strdup(this->identifier); + global_var_info->type = _type; + vpush(&context->global_variables, global_var_info); + + if (this->value) { + _fputs2(context->fd_data, this->identifier, ":\n"); + _fputsi(context->fd_data, "dq ", ((struct Integer*)(this->value->node_ptr))->value, "\n"); + } + else { + _fputs2(context->fd_bss, this->identifier, ":\n"); + _fputsi(context->fd_bss, "resb ", type_size(this->type, context), "\n"); + } +} + void compile_definition(struct Node *node, struct Definition *this, struct CPContext *context) { - if (this->type && !type_check(this->type, context)) { + if (this->type && !type_check(this->type, context)) { error_semantic("Type identifier was not declared in definition type", node); } struct TypeNode *_type; if (this->type && this->value) { - _type = compile_node(this->value, context); - if (this->type && !type_equal(_type, this->type, context)) { + _type = compile_node(this->value, context); + if (this->type && !type_equal(_type, this->type, context)) { error_semantic("Declared and inferred types are not equal", node); } } @@ -575,17 +655,30 @@ void compile_assignment(struct Node *node, struct Assignment *this, struct CPCon } struct Identifier *_identifier = (struct Identifier*)(this->dst->node_ptr); struct VariableInfo *var_info = context_find_variable(context, _identifier->identifier); - if (!var_info) { + struct GlobalVariableInfo *global_var_info = context_find_global_variable(context, _identifier->identifier); + if (!var_info && !global_var_info) { error_semantic("Variable was not declared in assignment", node); } - struct TypeNode *_type1 = var_info->type; + struct TypeNode *_type1; + if (var_info) { + _type1 = var_info->type; + } + else { + _type1 = global_var_info->type; + } struct TypeNode *_type2 = compile_node(this->src, context); if (!type_equal(_type1, _type2, context)) { error_semantic("Assignment of not equal types", node); } int tsize = type_size(_type1, context); - const char *dst = _concat3("[rbp + ", _itoa(var_info->sf_phase), "]"); + const char *dst; + if (var_info) { + dst = _concat3("[rbp + ", _itoa(var_info->sf_phase), "]"); + } + else { + dst = _concat3("[", global_var_info->name, "]"); + } const char *src = _concat3("[rsp - ", _itoa(align_to_word(tsize)), "]"); compile_memcpy(dst, src, tsize, context); } @@ -624,28 +717,38 @@ struct TypeNode *compile_identifier(struct Node *node, struct Identifier *this, _type = function_info->type; _fputs3(context->fd_text, "mov rax, ", function_info->name_back, "\n"); _fputs(context->fd_text, "mov [rsp - 8], rax\n"); + return _type; + } + struct VariableInfo *var_info = context_find_variable(context, this->identifier); + struct GlobalVariableInfo *global_var_info = context_find_global_variable(context, this->identifier); + if (!var_info && !global_var_info) { + error_semantic("Identifier was not declared", node); + } + if (this->address) { + if (var_info) { + _type = type_copy_node(var_info->type); + _fputsi(context->fd_text, "lea rax, [rbp + ", var_info->sf_phase, "]\n"); + _fputs(context->fd_text, "mov [rsp - 8], rax\n"); + } + else { + _type = type_copy_node(global_var_info->type); + _fputs3(context->fd_text, "mov qword [rsp - 8], ", global_var_info->name, "\n"); + } + _type->degree++; } else { - struct VariableInfo *var_info = context_find_variable(context, this->identifier); + const char *src; if (var_info) { - if (this->address) { - _type = type_copy_node(var_info->type); - _type->degree++; - _fputsi(context->fd_text, "lea rax, [rbp + ", var_info->sf_phase, "]\n"); - _fputs(context->fd_text, "mov [rsp - 8], rax\n"); - } - else { - _type = var_info->type; - const char *dst = _concat3("[rsp - ", _itoa(align_to_word(type_size(_type, context))), "]"); - const char *src = _concat3("[rbp + ", _itoa(var_info->sf_phase), "]"); - compile_memcpy(dst, src, align_to_word(type_size(_type, context)), context); - } + _type = var_info->type; + src = _concat3("[rbp + ", _itoa(var_info->sf_phase), "]"); } else { - error_semantic("Variable was not declared", node); + _type = global_var_info->type; + src = _concat3("[", global_var_info->name, "]"); } + const char *dst = _concat3("[rsp - ", _itoa(align_to_word(type_size(_type, context))), "]"); + compile_memcpy(dst, src, align_to_word(type_size(_type, context)), context); } - return _type; } @@ -762,7 +865,7 @@ struct TypeNode *compile_lambda_function(struct Node *node, struct LambdaFunctio context->function_index++; char *identifier_end = _concat("_E", identifier_back); - struct TypeNode *type = from_signature_to_type(this->signature); + struct TypeNode *type = from_signature_to_type(this->signature, context); struct TypeNode *_type = compile_function_signature(node, this->signature, context, this->block, identifier_back, identifier_end); if (!type_equal(_type, this->signature->return_type, context)) { @@ -813,11 +916,45 @@ struct TypeNode *compile_function_call(struct Node *node, struct FunctionCall *t error_semantic("Function expected in function call", node); } struct TypeFunction *_type = type->node_ptr; + + if (_type->propagate_allocator) { + vpush(&this->arguments, NULL); + int sz = vsize(&this->arguments); + for (int i = sz - 1; i >= 0; i--) { + this->arguments.ptr[i] = this->arguments.ptr[i - 1]; + } + if (this->propagate_allocator) { + this->arguments.ptr[0] = this->propagate_allocator; + } + else if (context_find_variable(context, "@")) { + struct Node *_node = (struct Node*)_malloc(sizeof(struct Node)); + _node->line_begin = 0; + _node->position_begin = 0; + _node->line_end = 0; + _node->position_end = 0; + _node->filename = "_generated"; + struct Identifier *identifier = (struct Identifier*)_malloc(sizeof(struct Identifier)); + _node->node_ptr = identifier; + _node->node_type = NodeIdentifier; + identifier->identifier = "@"; + identifier->address = false; + this->arguments.ptr[0] = _node; + } + else { + error_semantic("Allocator expected for propagation to called function", node); + } + } + else { + if (this->propagate_allocator) { + error_semantic("Unexpected allocator propagation to called function", node); + } + } + int sz = vsize(&_type->types); if (sz != vsize(&this->arguments)) { error_semantic("Incorrect number of arguments in function call", node); } - + for (int i = 0; i < sz; i++) { struct TypeNode *type_arg = compile_node(this->arguments.ptr[i], context); if (!type_equal(type_arg, _type->types.ptr[i], context)) { @@ -1243,15 +1380,19 @@ struct TypeNode *compile_node(struct Node *node, struct CPContext *context) { _fputi(context->fd_text, node->position_begin + 1); _fputs(context->fd_text, " -> "); - if (node->node_type == NodeBlock) { + if (node->node_type == NodeModule) { + _fputs(context->fd_text, "module\n"); + compile_module(node, (struct Module*)node->node_ptr, context); + } + else if (node->node_type == NodeBlock) { _fputs(context->fd_text, "block\n"); return compile_block(node, (struct Block*)node->node_ptr, context); } - if (node->node_type == NodeInclude) { + else if (node->node_type == NodeInclude) { _fputs(context->fd_text, "include\n"); compile_include(node, (struct Include*)node->node_ptr, context); } - if (node->node_type == NodeTest) { + else if (node->node_type == NodeTest) { _fputs(context->fd_text, "test\n"); compile_test(node, (struct Test*)node->node_ptr, context); } @@ -1271,6 +1412,10 @@ struct TypeNode *compile_node(struct Node *node, struct CPContext *context) { _fputs(context->fd_text, "prototype\n"); compile_prototype(node, (struct Prototype*)node->node_ptr, context); } + else if (node->node_type == NodeGlobalDefinition) { + _fputs(context->fd_text, "global definition\n"); + compile_global_definition(node, (struct GlobalDefinition*)node->node_ptr, context); + } else if (node->node_type == NodeDefinition) { _fputs(context->fd_text, "definition\n"); compile_definition(node, (struct Definition*)node->node_ptr, context); diff --git a/compiler/src/context.c b/compiler/src/context.c index 91effd3..6d945f6 100644 --- a/compiler/src/context.c +++ b/compiler/src/context.c @@ -3,6 +3,17 @@ #include #include +struct GlobalVariableInfo *context_find_global_variable(struct CPContext *context, const char *identifier) { + int sz = vsize(&context->global_variables); + for (int i = sz - 1; i >= 0; i--) { + struct GlobalVariableInfo *global_var_info = context->global_variables.ptr[i]; + if (_strcmp(global_var_info->name, identifier) == 0) { + return global_var_info; + } + } + return NULL; +} + struct VariableInfo *context_find_variable(struct CPContext *context, const char *identifier) { int sz = vsize(&context->variables); for (int i = sz - 1; i >= 0; i--) { diff --git a/compiler/src/lexer.c b/compiler/src/lexer.c index eae04b2..62d772e 100644 --- a/compiler/src/lexer.c +++ b/compiler/src/lexer.c @@ -179,6 +179,7 @@ struct TokenStream *lexer_process(const char *str, const char *filename) { else if (append_token(str, N, &i, "$", TokenDereference, true, line, &position, filename, token_stream)) {} else if (append_token(str, N, &i, "->", TokenGetField, true, line, &position, filename, token_stream)) {} else if (append_token(str, N, &i, "#", TokenSharp, true, line, &position, filename, token_stream)) {} + else if (append_token(str, N, &i, "@", TokenAt, true, line, &position, filename, token_stream)) {} else if (append_token(str, N, &i, "+", TokenPlus, true, line, &position, filename, token_stream)) {} else if (check_token(str, N, "-", i, true)) { if (i + 2 <= N && _isdigit(str[i + 1]) && @@ -403,6 +404,7 @@ const char *TokenColor(enum TokenType type, Color_Black, // TokenDereference, Color_Black, // TokenGetField, Color_Black, // TokenSharp, + Color_Black, // TokenAt, Color_Black, // TokenPlus, Color_Black, // TokenMinus, Color_Black, // TokenMult, diff --git a/compiler/src/syntax.c b/compiler/src/syntax.c index 426c930..4d75f4f 100644 --- a/compiler/src/syntax.c +++ b/compiler/src/syntax.c @@ -1,3 +1,4 @@ +#include "token.h" #include #include #include @@ -7,12 +8,13 @@ #include #include -struct Node *syntax_process_block (struct TokenStream *ts, struct Settings *st, bool braces); +struct Node *syntax_process_module (struct TokenStream *ts, struct Settings *st); +struct Node *syntax_process_block (struct TokenStream *ts, struct Settings *st); struct TypeNode *syntax_process_type (struct TokenStream *ts, struct Settings *st); struct Node *syntax_process_expression (struct TokenStream *ts, struct Settings *st); struct Node *syntax_process_primary (struct TokenStream *ts, struct Settings *st); struct FunctionSignature *syntax_process_function_signature (struct TokenStream *ts, struct Settings *st); -struct Node *syntax_process_statement (struct TokenStream *ts, struct Settings *st); +struct Node *syntax_process_statement (struct TokenStream *ts, struct Settings *st, bool top_level); struct Node *init_node(struct TokenStream *ts) { struct Node *node = (struct Node*)_malloc(sizeof(struct Node)); @@ -40,7 +42,24 @@ void pass_next(struct TokenStream *ts, enum TokenType type, const char *error) { tokenstream_next(ts); } -struct Node *syntax_process_block(struct TokenStream *ts, struct Settings *st, bool braces) { +struct Node *syntax_process_module(struct TokenStream *ts, struct Settings *st) { + struct Node *node = init_node(ts); + struct Module *this = (struct Module*)_malloc(sizeof(struct Module)); + node->node_ptr = this; + this->statement_list = vnew(); + node->node_type = NodeModule; + + while (tokenstream_get(ts).type != TokenEof) { + struct Node *node = syntax_process_statement(ts, st, true); + if (node) { + vpush(&this->statement_list, node); + } + } + finish_node(node, ts); + return node; +} + +struct Node *syntax_process_block(struct TokenStream *ts, struct Settings *st) { struct Node *node = init_node(ts); struct Block *this = (struct Block*)_malloc(sizeof(struct Block)); node->node_ptr = this; @@ -49,13 +68,11 @@ struct Node *syntax_process_block(struct TokenStream *ts, struct Settings *st, b node->node_type = NodeBlock; bool one_statement = false; - if (braces) { - if (tokenstream_get(ts).type == TokenBraceOpen) { - tokenstream_next(ts); - } - else { - one_statement = true; - } + if (tokenstream_get(ts).type == TokenBraceOpen) { + tokenstream_next(ts); + } + else { + one_statement = true; } if (tokenstream_get(ts).type == TokenDot) { @@ -74,7 +91,7 @@ struct Node *syntax_process_block(struct TokenStream *ts, struct Settings *st, b tokenstream_next(ts); defer = true; } - struct Node *node = syntax_process_statement(ts, st); + struct Node *node = syntax_process_statement(ts, st, false); if (node) { if (!defer) vpush(&this->statement_list, node); @@ -83,7 +100,7 @@ struct Node *syntax_process_block(struct TokenStream *ts, struct Settings *st, b } if (one_statement) break; } - if (braces && !one_statement) { + if (!one_statement) { if (tokenstream_get(ts).type != TokenBraceClose) { error_syntax("} expected after block", tokenstream_get(ts)); } @@ -504,7 +521,6 @@ struct Node *syntax_process_primary(struct TokenStream *ts, struct Settings *st) node->node_type = NodeLambdaFunction; node->node_ptr = this; tokenstream_next(ts); - pass_next(ts, TokenParenthesisOpen, "( expected in function definition"); this->signature = syntax_process_function_signature(ts, st); this->block = syntax_process_expression(ts, st); } @@ -515,9 +531,13 @@ struct Node *syntax_process_primary(struct TokenStream *ts, struct Settings *st) tokenstream_next(ts); this->type = syntax_process_type(ts, st); } - else if (tokenstream_get(ts).type == TokenIdentifier) { + else if (tokenstream_get(ts).type == TokenIdentifier || + tokenstream_get(ts).type == TokenAt) { struct Identifier *this = (struct Identifier*)_malloc(sizeof(struct Identifier)); - this->identifier = _strdup(tokenstream_get(ts).value_string); + if (tokenstream_get(ts).type == TokenIdentifier) + this->identifier = _strdup(tokenstream_get(ts).value_string); + else + this->identifier = _strdup("@"); node->node_ptr = this; node->node_type = NodeIdentifier; tokenstream_next(ts); @@ -530,7 +550,7 @@ struct Node *syntax_process_primary(struct TokenStream *ts, struct Settings *st) } } else if (tokenstream_get(ts).type == TokenBraceOpen) { - node = syntax_process_block(ts, st, true); + node = syntax_process_block(ts, st); } else if (tokenstream_get(ts).type == TokenIf) { struct If *this = (struct If*)_malloc(sizeof(struct If)); @@ -582,7 +602,7 @@ struct Node *syntax_process_primary(struct TokenStream *ts, struct Settings *st) pass_next(ts, TokenParenthesisOpen, "( expected in while condition"); struct Node *_expression = syntax_process_expression(ts, st); pass_next(ts, TokenParenthesisClose, ") expected in while condition"); - struct Node *_block = syntax_process_block(ts, st, true); + struct Node *_block = syntax_process_block(ts, st); this->condition = _expression; this->block = _block; if (tokenstream_get(ts).type == TokenElse) { @@ -640,6 +660,15 @@ struct Node *syntax_process_primary(struct TokenStream *ts, struct Settings *st) node->node_type = NodeFunctionCall; this->function = prv_node; this->arguments = vnew(); + this->propagate_allocator = NULL; + + if (tokenstream_get(ts).type == TokenAt) { + tokenstream_next(ts); + this->propagate_allocator = syntax_process_expression(ts, st); + if (tokenstream_get(ts).type != TokenParenthesisClose) { + pass_next(ts, TokenComma, ", expected in function call"); + } + } while (true) { if (tokenstream_get(ts).type == TokenParenthesisClose) { break; @@ -726,7 +755,13 @@ struct FunctionSignature *syntax_process_function_signature(struct TokenStream * struct FunctionSignature *this = (struct FunctionSignature*)_malloc(sizeof(struct FunctionSignature)); this->identifiers = vnew(); this->types = vnew(); + this->propagate_allocator = false; + if (tokenstream_get(ts).type == TokenAt) { + this->propagate_allocator = true; + tokenstream_next(ts); + } + pass_next(ts, TokenParenthesisOpen, "( expected in function prototype"); while (true) { if (tokenstream_get(ts).type == TokenParenthesisClose) { tokenstream_next(ts); @@ -748,17 +783,19 @@ struct FunctionSignature *syntax_process_function_signature(struct TokenStream * return this; } -struct Node *syntax_process_statement(struct TokenStream *ts, struct Settings *st) { +struct Node *syntax_process_statement(struct TokenStream *ts, struct Settings *st, bool top_level) { if (tokenstream_get(ts).type == TokenSemicolon) { tokenstream_next(ts); return NULL; } if (tokenstream_get(ts).type == TokenEval) { + if (top_level) error_syntax("Unexpected eval statement in top-level declaration", tokenstream_get(ts)); tokenstream_next(ts); return syntax_process_expression(ts, st); } struct Node *node = init_node(ts); if (tokenstream_get(ts).type == TokenInclude) { + if (!top_level) error_syntax("Include has to be a top-level declaration", tokenstream_get(ts)); struct Include *this = (struct Include*)_malloc(sizeof(struct Include)); node->node_ptr = this; node->node_type = NodeInclude; @@ -805,9 +842,9 @@ struct Node *syntax_process_statement(struct TokenStream *ts, struct Settings *s posix_close(fd); } struct Node *_node = process_parse(path, st); - struct Block *inc_block = (struct Block*)_node->node_ptr; - this->statement_list = inc_block->statement_list; - _free(inc_block); + struct Module *module = (struct Module*)_node->node_ptr; + this->statement_list = module->statement_list; + _free(module); _free(_node); tokenstream_next(ts); } @@ -847,7 +884,6 @@ struct Node *syntax_process_statement(struct TokenStream *ts, struct Settings *s check_next(ts, TokenIdentifier, "Identifier exprected in function definition"); this->name = _strdup(tokenstream_get(ts).value_string); tokenstream_next(ts); - pass_next(ts, TokenParenthesisOpen, "( expected in function definition"); this->signature = syntax_process_function_signature(ts, st); this->block = syntax_process_expression(ts, st); } @@ -866,26 +902,46 @@ struct Node *syntax_process_statement(struct TokenStream *ts, struct Settings *s check_next(ts, TokenIdentifier, "Identifier exprected in function prototype"); this->name = _strdup(tokenstream_get(ts).value_string); tokenstream_next(ts); - pass_next(ts, TokenParenthesisOpen, "( expected in function prototype"); this->signature = syntax_process_function_signature(ts, st); } else if (tokenstream_get(ts).type == TokenDef) { - struct Definition *this = (struct Definition*)_malloc(sizeof(struct Definition)); - node->node_ptr = this; - node->node_type = NodeDefinition; - tokenstream_next(ts); - check_next(ts, TokenIdentifier, "Identifier expected in definition statement"); - this->identifier = _strdup(tokenstream_get(ts).value_string); - tokenstream_next(ts); + if (top_level) { + struct GlobalDefinition *this = (struct GlobalDefinition*)_malloc(sizeof(struct GlobalDefinition)); + node->node_ptr = this; + node->node_type = NodeGlobalDefinition; + tokenstream_next(ts); + check_next(ts, TokenIdentifier, "Identifier expected in definition statement"); + this->identifier = _strdup(tokenstream_get(ts).value_string); + tokenstream_next(ts); - this->type = NULL; - this->value = NULL; - if (tokenstream_get(ts).type == TokenSharp) { - this->type = syntax_process_type(ts, st); + this->type = NULL; + this->value = NULL; + if (tokenstream_get(ts).type == TokenSharp) { + this->type = syntax_process_type(ts, st); + } + if (tokenstream_get(ts).type == TokenAssign) { + tokenstream_next(ts); + this->value = syntax_process_expression(ts, st); + } } - if (tokenstream_get(ts).type == TokenAssign) { + else { + struct Definition *this = (struct Definition*)_malloc(sizeof(struct Definition)); + node->node_ptr = this; + node->node_type = NodeDefinition; + tokenstream_next(ts); + check_next(ts, TokenIdentifier, "Identifier expected in definition statement"); + this->identifier = _strdup(tokenstream_get(ts).value_string); tokenstream_next(ts); - this->value = syntax_process_expression(ts, st); + + this->type = NULL; + this->value = NULL; + if (tokenstream_get(ts).type == TokenSharp) { + this->type = syntax_process_type(ts, st); + } + if (tokenstream_get(ts).type == TokenAssign) { + tokenstream_next(ts); + this->value = syntax_process_expression(ts, st); + } } } else if (tokenstream_get(ts).type == TokenTypedef) { @@ -899,6 +955,9 @@ struct Node *syntax_process_statement(struct TokenStream *ts, struct Settings *s pass_next(ts, TokenAssign, ":= expected in type definition statement"); this->type = syntax_process_type(ts, st); } + else if (top_level) { + error_syntax("Unexpected statement in top-level declaration", tokenstream_get(ts)); + } else if (tokenstream_get(ts).type == TokenReturn) { struct Return *this = (struct Return*)_malloc(sizeof(struct Return)); node->node_ptr = this; @@ -978,5 +1037,5 @@ struct Node *syntax_process_statement(struct TokenStream *ts, struct Settings *s struct Node *syntax_process(struct TokenStream *token_stream, struct Settings *st) { token_stream->pos = 0; - return syntax_process_block(token_stream, st, false); + return syntax_process_module(token_stream, st); } diff --git a/docs/altlibref.html b/docs/altlibref.html index a896d37..4572bb5 100644 --- a/docs/altlibref.html +++ b/docs/altlibref.html @@ -556,10 +556,6 @@

test_allocator

#1TestAllocator.init

Initializes an allocator with size size and allocates a buffer.
func ^#1TestAllocator.init(this #1TestAllocator, size #I) -> #V {
-    def PROT_READ := 1
-    def PROT_WRITE := 2
-    def MAP_PRIVATE := 2
-    def MAP_ANONYMOUS := 32
     this->data& <- posix_mmap(0 as #1I, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)
     this->size& <- 0
     this->reserved& <- size
diff --git a/docs/langref.html b/docs/langref.html
index 6b1d724..1519307 100644
--- a/docs/langref.html
+++ b/docs/langref.html
@@ -77,69 +77,76 @@ 

Table of Contents

  • Get address and movement
  • Dereference
  • -
  • Arithmetic +
  • Allocators
  • -
  • Block Expressions +
  • Arithmetic
  • -
  • Defer +
  • Block Expressions
  • -
  • Definitions +
  • Defer
  • -
  • Functions +
  • Definitions
  • -
  • If +
  • Functions
  • -
  • Lambda Functions +
  • If
  • -
  • Operators +
  • Lambda Functions
  • -
  • Structs +
  • Operators
  • -
  • Typedef +
  • Structs
  • -
  • While +
  • Typedef
  • +
  • While +
  • @@ -163,8 +170,61 @@

    Dereference

    return test_equal(ptr$, 7) }
    -

    Arithmetic

    -

    Addition and subtraction

    +

    Allocators

    +

    Allocator passing

    +If the function signature has @, then this function is an @-function, which expects an allocator to be passed. +The allocator can be referenced using @. +To call an @-function from a non-@-function (test is simply a function with empty set of arguments), the allocator has to be passed explicitly as first argument, prepended with @. +
    func .foo@() -> #I {
    +    return @.alloc(32) as #I
    +}
    +
    +test demo_allocator_passing {
    +    def allocator #TestAllocator
    +    eval allocator&.init(1024)
    +    defer eval allocator&.deinit()
    +    return test_equal(foo(@allocator&) = 0, 0)
    +}
    +
    +
    +

    Allocator propagation

    +An @-function can call both @-functions and non-@-functions. For an @-callee the allocator will be propagated. +
    func .inc(x #I) -> #I {
    +    return x + 1
    +}
    +
    +func .boo@() -> #I {
    +    return foo() + inc(4)
    +}
    +
    +test demo_allocator_propagation {
    +    def allocator #TestAllocator
    +    eval allocator&.init(1024)
    +    defer eval allocator&.deinit()
    +    return test_equal(boo(@allocator&) = 0, 0)
    +}
    +
    +
    +

    Allocator overriding

    +An @-function can call @-function with overriding its allocator with a new one. +
    func .doo@() -> #I {
    +    def allocator #TestAllocator
    +    eval allocator&.init(1024)
    +    defer eval allocator&.deinit()
    +    return foo(@allocator&)
    +}
    +
    +test demo_allocator_overriding {
    +    def allocator #TestAllocator
    +    eval allocator&.init(1024)
    +    defer eval allocator&.deinit()
    +    def x := boo(@allocator&)
    +    def y := doo(@allocator&)
    +    return test_equal((y - x) = 27, 0)
    +}
    +
    +

    Arithmetic

    +

    Addition and subtraction

    We can add with + operator and subtract with - operator.
    test demo_addition_and_subtraction {
         def a := 4 + 15             // now `a` is equal to 19
    @@ -174,7 +234,7 @@ 

    Addition and subtraction

    }
    -

    Multiplication and division

    +

    Multiplication and division

    We can multiply with * operator, divide with / operator and take modulo with % operator. These operators have higher precedence, than operators + and -.
    test demo_multiplication_and_division {
         def a := 4 + 3 * 9 + 6 * 2  // now `a` is equal to 43
    @@ -184,7 +244,7 @@ 

    Multiplication and division

    }
    -

    Parenthesis

    +

    Parenthesis

    We can set higher precedence to expression by enclosing it into parenthesis.
    test demo_parenthesis {
         def a := (4 + 3) * (9 + 6) * 2
    @@ -192,7 +252,7 @@ 

    Parenthesis

    }
    -

    Unary minus

    +

    Unary minus

    We can use - as a unary operator.
    test demo_unary_minus {
         def a := 5
    @@ -200,8 +260,8 @@ 

    Unary minus

    return test_equal(b, 3) }
    -

    Block Expressions

    -

    Return with expression

    +

    Block Expressions

    +

    Return with expression

    We can return value from a block using return statement. All return expressions have to have equal types.
    test demo_return_with_expression {
         def a := {
    @@ -211,7 +271,7 @@ 

    Return with expression

    }
    -

    Return to label

    +

    Return to label

    We can return to specific block using a label.
    test demo_return_to_label {
         def a := { .foo
    @@ -223,7 +283,7 @@ 

    Return to label

    }
    -

    Return void

    +

    Return void

    The expression {} is a block, which returns type #V. We can use it to break from blocks, which are used as statements (don't return anything and have type #V).
    test demo_return_from_block {
         def a := 3
    @@ -235,8 +295,8 @@ 

    Return void

    return test_equal(a, 2) }
    -

    Defer

    -

    Defer

    +

    Defer

    +

    Defer

    We can execute the statement at the end of the block, if we prepend it with defer.
    test demo_defer {
         defer return test_equal(a, 2)
    @@ -244,7 +304,7 @@ 

    Defer

    }
    -

    Multiple defer

    +

    Multiple defer

    All deferred statements are executed in reversed order.
    test demo_multiple_defer {
         defer return test_equal(a, 2)
    @@ -257,8 +317,8 @@ 

    Multiple defer

    }
    -

    Definitions

    -

    Definition

    +

    Definitions

    +

    Definition

    Use def to define a local variable. The variable must have an initial value after := operator.
    test demo_definition {
         def a := 5      // this is variable `a`, which has type `integer`
    @@ -267,7 +327,7 @@ 

    Definition

    }
    -

    Types

    +

    Types

    We can write types of local variables explicitly. The types begin with symbol #.
    test demo_types {
         def a #I := 5   // `#I` is an `integer` type
    @@ -276,16 +336,29 @@ 

    Types

    }
    -

    Type size

    +

    Type size

    Operator ^ returns size of the packed type in bytes. However, on stack data occupies more space, as it has to be word-aligned.
    test demo_type_size { .foo
         // The size of `#I` is 8 bytes
         // The size of `#C` is 1 byte
         return test_equal($#I + $#C, 9)
     }
    +
    +
    +

    Global variables

    +Variable can be created in global scope. If it is not assigned, it will be stored in .bss section and assigned to 0. If it is assigned, it has to be assigned to a simple integer and it will be stored in .data section. +
    def var1 #I
    +def var2 := 31;
    +
    +test demo_global_variable {
    +    var1 := 43
    +    var1& <- var1 + 6;
    +    return test_equal(var1 + var2, 80);
    +}
    +
     
    -

    Functions

    -

    Functions

    +

    Functions

    +

    Functions

    We can create functions. The function declaration has to be prepended with a .. To call a function, use parenthesis (). Functions are standard values, and we can store them in any structures. Function have their own types.
    func .add_one(x #I) -> #I {
         return x + 1
    @@ -299,7 +372,7 @@ 

    Functions

    }
    -

    Methods

    +

    Methods

    We can create methods for types. The method declaration has to be prepended with a caller type. The first argument of the method has to be the caller type. To call a method, use a . syntax. We can not store the method pointer in a variable (for example, this way: a := b.c()), since it will cause a capture of the caller value.
    func #I.add_one(x #I) -> #I {
         return x + 1
    @@ -318,7 +391,7 @@ 

    Methods

    }
    -

    Extern

    +

    Extern

    By default all functions are invisible to all source files, which include the file with the function declaration. In this case the function names are also generated, which makes it possible to have multiple functions with the same name in different scopes. We can make a function (or method) external by prepending ^ to its name. In this case, if it is a function, then the symbol will be equal to its name, and if it is a method, then the symbol will be mangled. It is not possible, however, to demonstrate it here.
    func ^.foo() -> #I 1
     
    @@ -337,8 +410,8 @@ 

    Extern

    return test_equal(a + b + c, 6) }
    -

    If

    -

    If statement

    +

    If

    +

    If statement

    The if statement consists of set of blocks and conditions. The last block may be without a condition.
    test demo_if_statement {
         def a := 3
    @@ -355,7 +428,7 @@ 

    If statement

    }
    -

    If expression

    +

    If expression

    The if can be used as expression. All branches of if have to return equal types, and also if has to have an else block, if it returns not #V.
    test demo_if_expression {
         def a := 3
    @@ -368,8 +441,8 @@ 

    If expression

    return test_equal(a, 2) }
    -

    Lambda Functions

    -

    Simple lambda functions

    +

    Lambda Functions

    +

    Simple lambda functions

    The lambda functions don't require to have name. But we can assign them to variables.
    test demo_lambda_functions {
         def f := \(x #I) -> #I {
    @@ -379,7 +452,7 @@ 

    Simple lambda functions

    }
    -

    Higher order functions

    +

    Higher order functions

    We can pass lambda functions to function calls.
    test demo_higher_order_functions {
         def twice := \(f #F(#I) -> #I, x #I) -> #I {
    @@ -388,8 +461,8 @@ 

    Higher order functions

    return test_equal(twice(\(x #I) -> #I x * x, 2), 16) }
    -

    Operators

    -

    Logic operators

    +

    Operators

    +

    Logic operators

    There are following logic operators: binary and, binary or, unary not. In any case both branches will be evaluated.
    test demo_logic_operators {
         def a := 1 and 1    // now `a` is equal to 1
    @@ -402,7 +475,7 @@ 

    Logic operators

    }
    -

    Bitwise operators

    +

    Bitwise operators

    There are following bitwise operators: binary and &, binary or |, binary xor ^, unary not ~, binary shift left <<, binary shift right >>. In any case both branches will be evaluated.
    test demo_bitwise_operators {
         def a := 14 & 7     // now `a` is equal 6
    @@ -415,7 +488,7 @@ 

    Bitwise operators

    }
    -

    Operator precendence

    +

    Operator precendence

    All operators are left-associative. The operators have following precedence. @@ -467,8 +540,8 @@

    Operator precendence

    or
    -

    Structs

    -

    Definition of struct

    +

    Structs

    +

    Definition of struct

    We declare instances of structs with .{ } syntax. The type of struct instance is inferred. Two struct types with different type names, but same type contents are equal. We can get the struct field value from a struct pointer with -> syntax. We can get the pointer to the struct field from a struct pointer with ->& syntax, which is often used to store value in a struct field with <- operator.
    typedef Pt := #S{ x: #I, y: #I }
     
    @@ -483,7 +556,7 @@ 

    Definition of struct

    }
    -

    Passing structs to functions

    +

    Passing structs to functions

    We can't pass structs to functions (currently). We can return struct from functions, but the implementation doesn't comply with the System V ABI.
    func .store_number(s #1Pt, n #I) -> #V {
         s->x& <- n
    @@ -495,8 +568,8 @@ 

    Passing structs to functions

    return test_equal(pt1&->x, 4) }
    -

    Typedef

    -

    Int type

    +

    Typedef

    +

    Int type

    We can give names to types.
    typedef T := #I
     
    @@ -509,7 +582,7 @@ 

    Int type

    }
    -

    Function type

    +

    Function type

    The function is also a type.
    typedef K := #F(#I) -> #I
     
    @@ -521,8 +594,8 @@ 

    Function type

    return test_equal(apply(add_one, 1), 2) }
    -

    While

    -

    While statement

    +

    While

    +

    While statement

    The while statement consists of block an condition.
    test demo_while_statement {
         def i := 0
    @@ -535,7 +608,7 @@ 

    While statement

    }
    -

    While expression

    +

    While expression

    The while can be used as expression. All break in while and an else block have to return equal types, and also while has to have an else block, if it returns not #V.
    test demo_while_expression {
         def i := 0
    @@ -550,7 +623,7 @@ 

    While expression

    }
    -

    While else block

    +

    While else block

    The else block will be executed, if we exit the loop when checking a condition, which is false.
    test demo_while_else_block {
         def i := 0
    @@ -565,7 +638,7 @@ 

    While else block

    }
    -

    While continue statement

    +

    While continue statement

    We can jump to the start of the loop using a continue statement.
    test demo_while_continue_statement {
         def i := 0
    @@ -581,7 +654,7 @@ 

    While continue statement

    }
    -

    While control with labels

    +

    While control with labels

    We can use break and continue not only to nearest loop, but also to given labeled loop.
    test demo_while_control_with_labels {
         def a := while .foo (1) {
    diff --git a/test/algo/merge_sort/main.al b/test/algo/merge_sort/main.al
    index e636a92..a899877 100644
    --- a/test/algo/merge_sort/main.al
    +++ b/test/algo/merge_sort/main.al
    @@ -2,13 +2,13 @@ include altlib."posix.al"
     include altlib."stdio.al"
     include altlib."test_allocator.al"
     
    -func .merge_sort(a #1I, lx #I, rx #I, allocator #1TestAllocator) -> #V {
    +func .merge_sort@(a #1I, lx #I, rx #I) -> #V {
         eval if (rx - lx > 1) {
             def m := (lx + rx) / 2
    -        eval merge_sort(a, lx, m, allocator)
    -        eval merge_sort(a, m, rx, allocator)
    +        eval merge_sort(a, lx, m)
    +        eval merge_sort(a, m, rx)
             
    -        def b := allocator.alloc((rx - lx) * $#I)
    +        def b := @.alloc((rx - lx) * $#I)
             def ptr1 := lx
             def ptr2 := m
             def ptr3 := 0
    @@ -44,7 +44,7 @@ func ^._start() -> #V {
             i := i + 1
         }
     
    -    eval merge_sort(a, 0, n, allocator&)
    +    eval merge_sort(@allocator&, a, 0, n)
     
         i := 0
         eval while (i < n) {
    diff --git a/test/unit/allocators.al b/test/unit/allocators.al
    new file mode 100644
    index 0000000..5f3159e
    --- /dev/null
    +++ b/test/unit/allocators.al
    @@ -0,0 +1,59 @@
    +func .test_equal(a #I, b #I) -> #I
    +    if (a = b) 0 else 1
    +
    +include altlib."test_allocator.al"
    +
    +//* Allocators
    +
    +//* Allocator passing
    +//* If the function signature has @, then this function is an @-function, which expects an allocator to be passed.
    +//* The allocator can be referenced using @.
    +//* To call an @-function from a non-@-function (test is simply a function with empty set of arguments), the allocator has to be passed explicitly as first argument, prepended with @.
    +
    +func .foo@() -> #I {
    +    return @.alloc(32) as #I
    +}
    +
    +test demo_allocator_passing {
    +    def allocator #TestAllocator
    +    eval allocator&.init(1024)
    +    defer eval allocator&.deinit()
    +    return test_equal(foo(@allocator&) = 0, 0)
    +}
    +
    +//* Allocator propagation
    +//* An @-function can call both @-functions and non-@-functions. For an @-callee the allocator will be propagated.
    +
    +func .inc(x #I) -> #I {
    +    return x + 1
    +}
    +
    +func .boo@() -> #I {
    +    return foo() + inc(4)
    +}
    +
    +test demo_allocator_propagation {
    +    def allocator #TestAllocator
    +    eval allocator&.init(1024)
    +    defer eval allocator&.deinit()
    +    return test_equal(boo(@allocator&) = 0, 0)
    +}
    +
    +//* Allocator overriding
    +//* An @-function can call @-function with overriding its allocator with a new one.
    +
    +func .doo@() -> #I {
    +    def allocator #TestAllocator
    +    eval allocator&.init(1024)
    +    defer eval allocator&.deinit()
    +    return foo(@allocator&)
    +}
    +
    +test demo_allocator_overriding {
    +    def allocator #TestAllocator
    +    eval allocator&.init(1024)
    +    defer eval allocator&.deinit()
    +    def x := boo(@allocator&)
    +    def y := doo(@allocator&)
    +    return test_equal((y - x) = 27, 0)
    +}
    diff --git a/test/unit/definitions.al b/test/unit/definitions.al
    index 558f3a2..2483a99 100644
    --- a/test/unit/definitions.al
    +++ b/test/unit/definitions.al
    @@ -29,3 +29,15 @@ test demo_type_size { .foo
         // The size of `#C` is 1 byte
         return test_equal($#I + $#C, 9)
     }
    +
    +//* Global variables
    +//* Variable can be created in global scope. If it is not assigned, it will be stored in `.bss` section and assigned to `0`. If it is assigned, it has to be assigned to a simple integer and it will be stored in `.data` section.
    +
    +def var1 #I
    +def var2 := 31;
    +
    +test demo_global_variable {
    +    var1 := 43
    +    var1& <- var1 + 6;
    +    return test_equal(var1 + var2, 80);
    +}