public inbox for ~johnnyrichard/olang-devel@lists.sr.ht
 help / color / mirror / code / Atom feed
* [PATCH olang v2 0/2] ast: refactor: inline union typedefs
@ 2024-08-13 18:55 Johnny Richard
  2024-08-13 18:55 ` [PATCH olang v2 1/2] ast: inline ast_literal_value_t union typedef Johnny Richard
                   ` (2 more replies)
  0 siblings, 3 replies; 81+ messages in thread
From: Johnny Richard @ 2024-08-13 18:55 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

In order to reduce noise and remove the extra step when accessing union
attributes in structs, I have inlined all 'typedef union' within the
structs.

This will make the code more readable.

Johnny Richard (2):
  ast: inline ast_literal_value_t union typedef
  ast: inline ast_node_data_t union typedef

 src/ast.c                   | 20 ++++++++++----------
 src/ast.h                   | 30 +++++++++++++-----------------
 src/codegen_linux_aarch64.c | 12 ++++++------
 src/codegen_linux_x86_64.c  | 12 ++++++------
 src/parser.c                |  4 ++--
 src/pretty_print_ast.c      | 14 +++++++-------
 tests/unit/parser_test.c    | 14 +++++++-------
 7 files changed, 51 insertions(+), 55 deletions(-)


base-commit: 36b028f712ff2402761ea307467860c346d3c0a0
-- 
2.46.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v2 1/2] ast: add function call node
@ 2024-09-27 23:07 Johnny Richard
  2024-09-27 21:11 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-09-27 23:07 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/ast.c     | 18 ++++++++++++++++++
 src/ast.h     | 12 ++++++++++++
 src/checker.c |  1 +
 3 files changed, 31 insertions(+)

diff --git a/src/ast.c b/src/ast.c
index dc2e019..db18426 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -60,6 +60,24 @@ ast_new_node_fn_def(arena_t *arena, string_view_t id, list_t *params, string_vie
     return node_fn_def;
 }
 
+ast_node_t *
+ast_new_node_fn_call(arena_t *arena, string_view_t id, list_t *args)
+{
+    assert(arena);
+    assert(args);
+
+    ast_node_t *node_fn_call = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
+    assert(node_fn_call);
+
+    node_fn_call->kind = AST_NODE_FN_CALL;
+    ast_fn_call_t *fn_call = &node_fn_call->as_fn_call;
+
+    fn_call->id = id;
+    fn_call->args = args;
+
+    return node_fn_call;
+}
+
 ast_node_t *
 ast_new_node_var_def(arena_t *arena, string_view_t id, string_view_t type, ast_node_t *value)
 {
diff --git a/src/ast.h b/src/ast.h
index 7ba431f..66c626d 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -32,6 +32,7 @@ typedef enum
     AST_NODE_TRANSLATION_UNIT,
     AST_NODE_BLOCK,
     AST_NODE_FN_DEF,
+    AST_NODE_FN_CALL,
     AST_NODE_VAR_DEF,
     AST_NODE_BINARY_OP,
     AST_NODE_RETURN_STMT,
@@ -66,6 +67,13 @@ typedef struct ast_fn_definition
     scope_t *scope;
 } ast_fn_definition_t;
 
+typedef struct ast_fn_call
+{
+    string_view_t id;
+    list_t *args;
+    scope_t *scope;
+} ast_fn_call_t;
+
 typedef struct ast_var_definition
 {
     string_view_t id;
@@ -142,6 +150,7 @@ typedef struct ast_node
     {
         ast_translation_unit_t as_translation_unit;
         ast_fn_definition_t as_fn_def;
+        ast_fn_call_t as_fn_call;
         ast_var_definition_t as_var_def;
         ast_binary_op_t as_bin_op;
         ast_literal_t as_literal;
@@ -158,6 +167,9 @@ ast_new_translation_unit(arena_t *arena);
 ast_node_t *
 ast_new_node_fn_def(arena_t *arena, string_view_t id, list_t *params, string_view_t return_type, ast_node_t *block);
 
+ast_node_t *
+ast_new_node_fn_call(arena_t *arena, string_view_t id, list_t *args);
+
 ast_node_t *
 ast_new_node_var_def(arena_t *arena, string_view_t id, string_view_t type, ast_node_t *value);
 
diff --git a/src/checker.c b/src/checker.c
index 814d052..fd5bda7 100644
--- a/src/checker.c
+++ b/src/checker.c
@@ -127,6 +127,7 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
 
         case AST_NODE_LITERAL:
         case AST_NODE_UNKNOWN:
+        case AST_NODE_FN_CALL:
             return;
     }
 }
-- 
2.46.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v1 2/2] parser: add support for parsing function calls
@ 2024-09-25 23:20 Johnny Richard
  2024-09-25 21:22 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-09-25 23:20 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/checker.c                   | 29 ++++++++++++++--
 src/parser.c                    | 59 +++++++++++++++++++++++++++++++--
 src/pretty_print_ast.c          | 17 ++++++++++
 tests/olc/0028_function_call.ol |  7 ++--
 4 files changed, 105 insertions(+), 7 deletions(-)

diff --git a/src/checker.c b/src/checker.c
index 814d052..7c3767f 100644
--- a/src/checker.c
+++ b/src/checker.c
@@ -64,12 +64,37 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
         }
 
         case AST_NODE_FN_DEF: {
-            ast->as_fn_def.scope = scope;
-            // FIXME: insert function symbol to scope
+            ast_fn_definition_t *fn_def = &ast->as_fn_def;
+            fn_def->scope = scope;
+
+            list_item_t *item = list_head(fn_def->params);
+
+            while (item != NULL) {
+                ast_fn_param_t *param = (ast_fn_param_t *)item->value;
+
+                symbol_t *symbol = symbol_new(checker->arena, param->id, type_from_id(param->type_id));
+                scope_insert(scope, symbol);
+
+                item = list_next(item);
+            }
+
             populate_scope(checker, scope, ast->as_fn_def.block);
             return;
         }
 
+        case AST_NODE_FN_CALL: {
+            ast->as_fn_call.scope = scope;
+
+            list_item_t *item = list_head(ast->as_fn_call.args);
+
+            while (item != NULL) {
+                populate_scope(checker, scope, (ast_node_t *)item->value);
+                item = list_next(item);
+            }
+
+            return;
+        }
+
         case AST_NODE_IF_STMT: {
             populate_scope(checker, scope, ast->as_if_stmt.cond);
             populate_scope(checker, scope, ast->as_if_stmt.then);
diff --git a/src/parser.c b/src/parser.c
index fba7b72..a025ed4 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -52,6 +52,9 @@ parser_parse_var_def(parser_t *parser);
 static ast_node_t *
 parser_parse_fn_definition(parser_t *parser);
 
+static list_t *
+parser_parse_fn_args(parser_t *parser);
+
 static list_t *
 parser_parse_fn_params(parser_t *parser);
 
@@ -253,8 +256,18 @@ parser_parse_factor(parser_t *parser)
         case TOKEN_NUMBER:
             return ast_new_node_literal_u32(parser->arena, string_view_to_u32(token.value));
 
-        case TOKEN_ID:
-            return ast_new_node_ref(parser->arena, token.value);
+        case TOKEN_ID: {
+            string_view_t id = token.value;
+
+            lexer_peek_next(parser->lexer, &token);
+
+            if (token.kind == TOKEN_OPAREN) {
+                list_t *args = parser_parse_fn_args(parser);
+                return ast_new_node_fn_call(parser->arena, id, args);
+            }
+
+            return ast_new_node_ref(parser->arena, id);
+        }
 
         case TOKEN_OPAREN: {
             ast_node_t *expr = parser_parse_expr(parser);
@@ -275,6 +288,48 @@ parser_parse_factor(parser_t *parser)
     }
 }
 
+static list_t *
+parser_parse_fn_args(parser_t *parser)
+{
+    if (!skip_expected_token(parser, TOKEN_OPAREN)) {
+        return NULL;
+    }
+
+    list_t *args = arena_alloc(parser->arena, sizeof(list_t));
+    if (args == NULL) {
+        fprintf(stderr, "[FATAL] Out of memory: parser_parse_fn_args: %s\n", strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+
+    list_init(args, parser->arena);
+
+    skip_line_feeds(parser->lexer);
+
+    token_t token;
+    lexer_peek_next(parser->lexer, &token);
+
+    bool is_not_first_arg = false;
+
+    while (token.kind != TOKEN_CPAREN && token.kind != TOKEN_EOF) {
+        if (is_not_first_arg && expected_token(parser, &token, TOKEN_COMMA)) {
+            lexer_next_token(parser->lexer, &token);
+        }
+
+        ast_node_t *expr = parser_parse_expr(parser);
+        list_append(args, expr);
+
+        skip_line_feeds(parser->lexer);
+        lexer_peek_next(parser->lexer, &token);
+        is_not_first_arg = true;
+    }
+
+    if (!skip_expected_token(parser, TOKEN_CPAREN)) {
+        return NULL;
+    }
+
+    return args;
+}
+
 static list_t *
 parser_parse_fn_params(parser_t *parser)
 {
diff --git a/src/pretty_print_ast.c b/src/pretty_print_ast.c
index b53ea5c..2541544 100644
--- a/src/pretty_print_ast.c
+++ b/src/pretty_print_ast.c
@@ -162,6 +162,23 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
             list_append(node->children, block);
             return node;
         }
+        case AST_NODE_FN_CALL: {
+            pretty_print_node_t *node = pretty_print_node_new(arena);
+            ast_fn_call_t fn_call = ast->as_fn_call;
+
+            char name[256];
+            sprintf(name, "Function_Call <name:" SV_FMT ">", SV_ARG(fn_call.id));
+            node->name = (char *)arena_alloc(arena, sizeof(char) * (strlen(name) + 1));
+            strcpy(node->name, name);
+
+            list_item_t *item = list_head(fn_call.args);
+            while (item != NULL) {
+                list_append(node->children, ast_node_to_pretty_print_node(item->value, arena));
+                item = list_next(item);
+            }
+
+            return node;
+        }
         case AST_NODE_BLOCK: {
             pretty_print_node_t *node = pretty_print_node_new(arena);
             ast_block_t block = ast->as_block;
diff --git a/tests/olc/0028_function_call.ol b/tests/olc/0028_function_call.ol
index ccadc0d..cfaa969 100644
--- a/tests/olc/0028_function_call.ol
+++ b/tests/olc/0028_function_call.ol
@@ -14,8 +14,7 @@
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 fn main(): u8 {
-  # TODO: call the function once function call is implemented
-  return 0
+  return add(40, 2) 
 }
 
 fn add(a: u32, b: u32): u8 {
@@ -27,7 +26,9 @@ fn add(a: u32, b: u32): u8 {
 # |-Function_Definition <name:main> <return:u8>
 # | `-Block
 # |   `-Return_Statement
-# |     `-Literal <kind:u32> <value:0>
+# |     `-Function_Call <name:add>
+# |       |-Literal <kind:u32> <value:40>
+# |       `-Literal <kind:u32> <value:2>
 # `-Function_Definition <name:add> <return:u8>
 #   |-Param_Definition <name:a> <type:u32>
 #   |-Param_Definition <name:b> <type:u32>
-- 
2.46.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang] tests: fix diff error output
@ 2024-09-25 18:39 Carlos Maniero
  2024-09-25 18:39 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-09-25 18:39 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

When passing the expected file instead of the actual file as first
argument on the diff, it provides a more clear output. Given that the
application returned a new line that the test was not expecting it was
returning the follow output:

- new line (in red)

When it is easier to understand the error when it shows as:

+ new line (in green)

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 tests/olc/run.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/olc/run.sh b/tests/olc/run.sh
index 529f0f5..503f960 100755
--- a/tests/olc/run.sh
+++ b/tests/olc/run.sh
@@ -108,7 +108,7 @@ diff_output() {
   fi
 
   print_failed "match failed"
-  diff "$actual_file" "$expected_file" -u --color
+  diff "$expected_file" "$actual_file" -u --color
   exit 1
 }
 

base-commit: 75cfabf7da32ae460c6ecb4d4a705f4705fc2c86
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang] parser: parse multiple function into a single translation unit
@ 2024-09-25 18:30 Carlos Maniero
  2024-09-25 18:31 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-09-25 18:30 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

The parser was expecting a single function per translation unit, now it
accepts many or none.

It still only allowing functions definitions on translation units, soon
variables and constants should also be allowed.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 src/parser.c                    | 21 +++++++++++++-----
 tests/olc/0028_function_call.ol | 39 +++++++++++++++++++++++++++++++++
 2 files changed, 54 insertions(+), 6 deletions(-)
 create mode 100644 tests/olc/0028_function_call.ol

diff --git a/src/parser.c b/src/parser.c
index 11bb1cd..fba7b72 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -78,15 +78,24 @@ parser_init(parser_t *parser, lexer_t *lexer, arena_t *arena, char *file_path)
 ast_node_t *
 parser_parse_translation_unit(parser_t *parser)
 {
+    token_t token;
+    ast_node_t *translation_unit_node = ast_new_translation_unit(parser->arena);
+
     skip_line_feeds(parser->lexer);
-    ast_node_t *fn = parser_parse_fn_definition(parser);
-    if (fn == NULL) {
-        return NULL;
-    }
+    lexer_peek_next(parser->lexer, &token);
 
-    ast_node_t *translation_unit_node = ast_new_translation_unit(parser->arena);
+    while (token.kind != TOKEN_EOF) {
+        ast_node_t *fn = parser_parse_fn_definition(parser);
 
-    list_append(translation_unit_node->as_translation_unit.decls, fn);
+        if (fn == NULL) {
+            return NULL;
+        }
+
+        list_append(translation_unit_node->as_translation_unit.decls, fn);
+
+        skip_line_feeds(parser->lexer);
+        lexer_peek_next(parser->lexer, &token);
+    }
 
     return translation_unit_node;
 }
diff --git a/tests/olc/0028_function_call.ol b/tests/olc/0028_function_call.ol
new file mode 100644
index 0000000..ccadc0d
--- /dev/null
+++ b/tests/olc/0028_function_call.ol
@@ -0,0 +1,39 @@
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+fn main(): u8 {
+  # TODO: call the function once function call is implemented
+  return 0
+}
+
+fn add(a: u32, b: u32): u8 {
+  return a + b
+}
+
+# TEST test_ast WITH
+# Translation_Unit
+# |-Function_Definition <name:main> <return:u8>
+# | `-Block
+# |   `-Return_Statement
+# |     `-Literal <kind:u32> <value:0>
+# `-Function_Definition <name:add> <return:u8>
+#   |-Param_Definition <name:a> <type:u32>
+#   |-Param_Definition <name:b> <type:u32>
+#   `-Block
+#     `-Return_Statement
+#       `-Binary_Operation (+)
+#         |-Reference <name:a>
+#         `-Reference <name:b>
+# END

base-commit: 75cfabf7da32ae460c6ecb4d4a705f4705fc2c86
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v1 2/3] lexer: add token comma
@ 2024-09-23 22:19 Johnny Richard
  2024-09-23 22:23 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-09-23 22:19 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/lexer.c                            |  6 ++++
 src/lexer.h                            |  1 +
 tests/olc/0027_function_with_params.ol | 41 ++++++++++++++++++++++++++
 3 files changed, 48 insertions(+)
 create mode 100644 tests/olc/0027_function_with_params.ol

diff --git a/src/lexer.c b/src/lexer.c
index ebc21b7..6fe0151 100644
--- a/src/lexer.c
+++ b/src/lexer.c
@@ -227,6 +227,11 @@ lexer_next_token(lexer_t *lexer, token_t *token)
                 lexer_skip_char(lexer);
                 return;
             }
+            case ',': {
+                lexer_init_char_value_token(lexer, token, TOKEN_COMMA);
+                lexer_skip_char(lexer);
+                return;
+            }
             case '{': {
                 lexer_init_char_value_token(lexer, token, TOKEN_OCURLY);
                 lexer_skip_char(lexer);
@@ -289,6 +294,7 @@ static char *token_kind_str_table[] = {
     [TOKEN_OPAREN] = "(",
     [TOKEN_CPAREN] = ")",
     [TOKEN_COLON] = ":",
+    [TOKEN_COMMA] = ",",
     [TOKEN_OCURLY] = "{",
     [TOKEN_CCURLY] = "}",
     [TOKEN_PLUS] = "+",
diff --git a/src/lexer.h b/src/lexer.h
index 717d21d..2746e3e 100644
--- a/src/lexer.h
+++ b/src/lexer.h
@@ -73,6 +73,7 @@ typedef enum token_kind
     TOKEN_OPAREN,
     TOKEN_CPAREN,
     TOKEN_COLON,
+    TOKEN_COMMA,
     TOKEN_OCURLY,
     TOKEN_CCURLY,
     TOKEN_EOF
diff --git a/tests/olc/0027_function_with_params.ol b/tests/olc/0027_function_with_params.ol
new file mode 100644
index 0000000..f70fe7c
--- /dev/null
+++ b/tests/olc/0027_function_with_params.ol
@@ -0,0 +1,41 @@
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+fn main(argc: u8, argv: u64): u8 {
+  return 0
+}
+
+# TEST test_contains_tokens WITH
+# ./0027_function_with_params.ol:16:1: <fn>
+# ./0027_function_with_params.ol:16:4: <identifier>
+# ./0027_function_with_params.ol:16:8: <(>
+# ./0027_function_with_params.ol:16:9: <identifier>
+# ./0027_function_with_params.ol:16:13: <:>
+# ./0027_function_with_params.ol:16:15: <identifier>
+# ./0027_function_with_params.ol:16:17: <,>
+# ./0027_function_with_params.ol:16:19: <identifier>
+# ./0027_function_with_params.ol:16:23: <:>
+# ./0027_function_with_params.ol:16:25: <identifier>
+# ./0027_function_with_params.ol:16:28: <)>
+# ./0027_function_with_params.ol:16:29: <:>
+# ./0027_function_with_params.ol:16:31: <identifier>
+# ./0027_function_with_params.ol:16:34: <{>
+# ./0027_function_with_params.ol:16:35: <line_feed>
+# ./0027_function_with_params.ol:17:3: <return>
+# ./0027_function_with_params.ol:17:10: <number>
+# ./0027_function_with_params.ol:17:11: <line_feed>
+# ./0027_function_with_params.ol:18:1: <}>
+# END
+
-- 
2.46.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang 2/2] ast: permit multi declarations on translation unit
@ 2024-09-23 11:43 Carlos Maniero
  2024-09-23 11:44 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-09-23 11:43 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

At this point the parser still parsing only a single function, but the
ast is ready to support multiple declarations.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 src/ast.c                   |  7 +++++--
 src/ast.h                   |  4 ++--
 src/checker.c               |  7 ++++++-
 src/codegen_linux_aarch64.c | 22 +++++++++++++++++++---
 src/codegen_linux_x86_64.c  | 22 +++++++++++++++++++---
 src/parser.c                |  6 +++++-
 src/pretty_print_ast.c      | 13 +++++++++++--
 tests/unit/parser_test.c    | 11 ++++++++---
 8 files changed, 75 insertions(+), 17 deletions(-)

diff --git a/src/ast.c b/src/ast.c
index f5fa483..bb74679 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -23,7 +23,7 @@
 #include "string_view.h"
 
 ast_node_t *
-ast_new_translation_unit(arena_t *arena, ast_node_t *fn_def)
+ast_new_translation_unit(arena_t *arena)
 {
     ast_node_t *node = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
     assert(node);
@@ -31,7 +31,10 @@ ast_new_translation_unit(arena_t *arena, ast_node_t *fn_def)
     node->kind = AST_NODE_TRANSLATION_UNIT;
     ast_translation_unit_t *translation_unit = &node->as_translation_unit;
 
-    translation_unit->fn = fn_def;
+    translation_unit->decls = (list_t *)arena_alloc(arena, sizeof(list_t));
+    assert(translation_unit->decls);
+
+    list_init(translation_unit->decls, arena);
 
     return node;
 }
diff --git a/src/ast.h b/src/ast.h
index 718c80f..6cfbfc0 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -48,7 +48,7 @@ typedef struct ast_block
 
 typedef struct ast_translation_unit
 {
-    ast_node_t *fn;
+    list_t *decls;
 } ast_translation_unit_t;
 
 typedef struct ast_fn_definition
@@ -146,7 +146,7 @@ typedef struct ast_node
 } ast_node_t;
 
 ast_node_t *
-ast_new_translation_unit(arena_t *arena, ast_node_t *fn_def);
+ast_new_translation_unit(arena_t *arena);
 
 ast_node_t *
 ast_new_node_fn_def(arena_t *arena, string_view_t id, string_view_t return_type, ast_node_t *block);
diff --git a/src/checker.c b/src/checker.c
index e9bfacb..814d052 100644
--- a/src/checker.c
+++ b/src/checker.c
@@ -54,7 +54,12 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
 {
     switch (ast->kind) {
         case AST_NODE_TRANSLATION_UNIT: {
-            populate_scope(checker, scope, ast->as_translation_unit.fn);
+            list_item_t *item = list_head(ast->as_translation_unit.decls);
+
+            while (item != NULL) {
+                populate_scope(checker, scope, (ast_node_t *)item->value);
+                item = list_next(item);
+            }
             return;
         }
 
diff --git a/src/codegen_linux_aarch64.c b/src/codegen_linux_aarch64.c
index e8ae729..d8187ab 100644
--- a/src/codegen_linux_aarch64.c
+++ b/src/codegen_linux_aarch64.c
@@ -49,10 +49,26 @@ codegen_linux_aarch64_emit_translation_unit(FILE *out, ast_node_t *node)
     assert(node->kind == AST_NODE_TRANSLATION_UNIT);
     ast_translation_unit_t translation_unit = node->as_translation_unit;
 
-    ast_fn_definition_t fn = translation_unit.fn->as_fn_def;
+    list_item_t *item = list_head(translation_unit.decls);
 
-    assert(string_view_eq_to_cstr(fn.id, "main"));
-    codegen_linux_aarch64_emit_function(out, &fn);
+    bool main_found = false;
+
+    while (item != NULL) {
+        ast_node_t *decl = (ast_node_t *)item->value;
+
+        if (decl->kind == AST_NODE_FN_DEF) {
+            ast_fn_definition_t fn = decl->as_fn_def;
+            codegen_linux_aarch64_emit_function(out, &fn);
+
+            main_found = main_found || string_view_eq_to_cstr(fn.id, "main");
+        } else {
+            assert(0 && "translation unit only supports function declarations");
+        }
+
+        item = list_next(item);
+    }
+
+    assert(main_found && "main function is required.");
 }
 
 static void
diff --git a/src/codegen_linux_x86_64.c b/src/codegen_linux_x86_64.c
index 37d4575..0173443 100644
--- a/src/codegen_linux_x86_64.c
+++ b/src/codegen_linux_x86_64.c
@@ -63,10 +63,26 @@ codegen_linux_x86_64_emit_translation_unit(codegen_x86_64_t *codegen, ast_node_t
     assert(node->kind == AST_NODE_TRANSLATION_UNIT);
     ast_translation_unit_t translation_unit = node->as_translation_unit;
 
-    ast_fn_definition_t fn = translation_unit.fn->as_fn_def;
+    list_item_t *item = list_head(translation_unit.decls);
 
-    assert(string_view_eq_to_cstr(fn.id, "main"));
-    codegen_linux_x86_64_emit_function(codegen, &fn);
+    bool main_found = false;
+
+    while (item != NULL) {
+        ast_node_t *decl = (ast_node_t *)item->value;
+
+        if (decl->kind == AST_NODE_FN_DEF) {
+            ast_fn_definition_t fn = decl->as_fn_def;
+            codegen_linux_x86_64_emit_function(codegen, &fn);
+
+            main_found = main_found || string_view_eq_to_cstr(fn.id, "main");
+        } else {
+            assert(0 && "translation unit only supports function declarations");
+        }
+
+        item = list_next(item);
+    }
+
+    assert(main_found && "main function is required.");
 }
 
 static void
diff --git a/src/parser.c b/src/parser.c
index 9332f6e..c79f3bd 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -80,7 +80,11 @@ parser_parse_translation_unit(parser_t *parser)
         return NULL;
     }
 
-    return ast_new_translation_unit(parser->arena, fn);
+    ast_node_t *translation_unit_node = ast_new_translation_unit(parser->arena);
+
+    list_append(translation_unit_node->as_translation_unit.decls, fn);
+
+    return translation_unit_node;
 }
 
 static ast_binary_op_kind_t
diff --git a/src/pretty_print_ast.c b/src/pretty_print_ast.c
index db646c5..8116e60 100644
--- a/src/pretty_print_ast.c
+++ b/src/pretty_print_ast.c
@@ -116,8 +116,17 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
             pretty_print_node_t *node = pretty_print_node_new(arena);
             node->name = "Translation_Unit";
 
-            pretty_print_node_t *fn_node = ast_node_to_pretty_print_node(ast->as_translation_unit.fn, arena);
-            list_append(node->children, fn_node);
+            list_item_t *item = list_head(ast->as_translation_unit.decls);
+
+            while (item != NULL) {
+                ast_node_t *decl = (ast_node_t *)item->value;
+
+                pretty_print_node_t *fn_node = ast_node_to_pretty_print_node(decl, arena);
+                list_append(node->children, fn_node);
+
+                item = list_next(item);
+            }
+
             return node;
         }
         case AST_NODE_FN_DEF: {
diff --git a/tests/unit/parser_test.c b/tests/unit/parser_test.c
index 4e229be..a7c60d1 100644
--- a/tests/unit/parser_test.c
+++ b/tests/unit/parser_test.c
@@ -46,10 +46,15 @@ parse_translation_unit_test(const MunitParameter params[], void *user_data_or_fi
     assert_uint(translation_unit_node->kind, ==, AST_NODE_TRANSLATION_UNIT);
 
     ast_translation_unit_t translation_unit = translation_unit_node->as_translation_unit;
-    assert_not_null(translation_unit.fn);
-    assert_uint(translation_unit.fn->kind, ==, AST_NODE_FN_DEF);
 
-    ast_fn_definition_t fn = translation_unit.fn->as_fn_def;
+    assert_uint(list_size(translation_unit.decls), ==, 1);
+
+    ast_node_t *fn_node = (ast_node_t *)list_head(translation_unit.decls)->value;
+
+    assert_not_null(fn_node);
+    assert_uint(fn_node->kind, ==, AST_NODE_FN_DEF);
+
+    ast_fn_definition_t fn = fn_node->as_fn_def;
     assert_memory_equal(fn.id.size, fn.id.chars, "main");
     assert_memory_equal(fn.return_type.size, fn.return_type.chars, "u32");
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v1 3/3] naming: rename all identifier symbols to id
@ 2024-09-23 10:11 Carlos Maniero
  2024-09-23 10:12 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-09-23 10:11 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

We were already using this pattern but initially we were callig them the
full name.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 src/ast.c                   | 12 ++++++------
 src/ast.h                   | 12 ++++++------
 src/checker.c               |  2 +-
 src/codegen_linux_aarch64.c |  4 ++--
 src/codegen_linux_x86_64.c  |  8 ++++----
 src/lexer.c                 |  4 ++--
 src/lexer.h                 |  2 +-
 src/parser.c                | 12 ++++++------
 src/pretty_print_ast.c      |  6 +++---
 tests/unit/parser_test.c    |  2 +-
 10 files changed, 32 insertions(+), 32 deletions(-)

diff --git a/src/ast.c b/src/ast.c
index a136182..7019316 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -37,7 +37,7 @@ ast_new_program(arena_t *arena, ast_node_t *fn_def)
 }
 
 ast_node_t *
-ast_new_node_fn_def(arena_t *arena, string_view_t identifier, string_view_t return_type, ast_node_t *block)
+ast_new_node_fn_def(arena_t *arena, string_view_t id, string_view_t return_type, ast_node_t *block)
 {
     ast_node_t *node_fn_def = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
     assert(node_fn_def);
@@ -45,7 +45,7 @@ ast_new_node_fn_def(arena_t *arena, string_view_t identifier, string_view_t retu
     node_fn_def->kind = AST_NODE_FN_DEF;
     ast_fn_definition_t *fn_def = &node_fn_def->as_fn_def;
 
-    fn_def->identifier = identifier;
+    fn_def->id = id;
     fn_def->return_type = return_type;
     fn_def->block = block;
 
@@ -53,7 +53,7 @@ ast_new_node_fn_def(arena_t *arena, string_view_t identifier, string_view_t retu
 }
 
 ast_node_t *
-ast_new_node_var_def(arena_t *arena, string_view_t identifier, string_view_t type, ast_node_t *value)
+ast_new_node_var_def(arena_t *arena, string_view_t id, string_view_t type, ast_node_t *value)
 {
     ast_node_t *node_var_def = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
     assert(node_var_def);
@@ -61,7 +61,7 @@ ast_new_node_var_def(arena_t *arena, string_view_t identifier, string_view_t typ
     node_var_def->kind = AST_NODE_VAR_DEF;
     ast_var_definition_t *var_def = &node_var_def->as_var_def;
 
-    var_def->identifier = identifier;
+    var_def->id = id;
     var_def->type = type;
     var_def->value = value;
 
@@ -96,13 +96,13 @@ ast_new_node_literal_u32(arena_t *arena, uint32_t value)
 }
 
 ast_node_t *
-ast_new_node_ref(arena_t *arena, string_view_t identifier)
+ast_new_node_ref(arena_t *arena, string_view_t id)
 {
     ast_node_t *node_ref = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
     assert(node_ref);
 
     node_ref->kind = AST_NODE_REF;
-    node_ref->as_ref.identifier = identifier;
+    node_ref->as_ref.id = id;
 
     return node_ref;
 }
diff --git a/src/ast.h b/src/ast.h
index e618314..df65e59 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -53,7 +53,7 @@ typedef struct ast_program
 
 typedef struct ast_fn_definition
 {
-    string_view_t identifier;
+    string_view_t id;
     string_view_t return_type;
     ast_node_t *block;
     scope_t *scope;
@@ -61,7 +61,7 @@ typedef struct ast_fn_definition
 
 typedef struct ast_var_definition
 {
-    string_view_t identifier;
+    string_view_t id;
     string_view_t type;
     ast_node_t *value;
     scope_t *scope;
@@ -83,7 +83,7 @@ typedef struct ast_literal
 
 typedef struct ast_ref
 {
-    string_view_t identifier;
+    string_view_t id;
     scope_t *scope;
 } ast_ref_t;
 
@@ -149,10 +149,10 @@ ast_node_t *
 ast_new_program(arena_t *arena, ast_node_t *fn_def);
 
 ast_node_t *
-ast_new_node_fn_def(arena_t *arena, string_view_t identifier, string_view_t return_type, ast_node_t *block);
+ast_new_node_fn_def(arena_t *arena, string_view_t id, string_view_t return_type, ast_node_t *block);
 
 ast_node_t *
-ast_new_node_var_def(arena_t *arena, string_view_t identifier, string_view_t type, ast_node_t *value);
+ast_new_node_var_def(arena_t *arena, string_view_t id, string_view_t type, ast_node_t *value);
 
 ast_node_t *
 ast_new_node_bin_op(arena_t *arena, ast_binary_op_kind_t kind, ast_node_t *lhs, ast_node_t *rhs);
@@ -161,7 +161,7 @@ ast_node_t *
 ast_new_node_literal_u32(arena_t *arena, uint32_t value);
 
 ast_node_t *
-ast_new_node_ref(arena_t *arena, string_view_t identifier);
+ast_new_node_ref(arena_t *arena, string_view_t id);
 
 ast_node_t *
 ast_new_node_return_stmt(arena_t *arena, ast_node_t *expr);
diff --git a/src/checker.c b/src/checker.c
index def7e86..090920c 100644
--- a/src/checker.c
+++ b/src/checker.c
@@ -106,7 +106,7 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
         }
 
         case AST_NODE_VAR_DEF: {
-            string_view_t id = ast->as_var_def.identifier;
+            string_view_t id = ast->as_var_def.id;
 
             symbol_t *symbol = symbol_new(checker->arena, id, type_from_id(ast->as_var_def.type));
 
diff --git a/src/codegen_linux_aarch64.c b/src/codegen_linux_aarch64.c
index 18173ce..93dde4f 100644
--- a/src/codegen_linux_aarch64.c
+++ b/src/codegen_linux_aarch64.c
@@ -51,7 +51,7 @@ codegen_linux_aarch64_emit_program(FILE *out, ast_node_t *node)
 
     ast_fn_definition_t fn = program.fn->as_fn_def;
 
-    assert(string_view_eq_to_cstr(fn.identifier, "main"));
+    assert(string_view_eq_to_cstr(fn.id, "main"));
     codegen_linux_aarch64_emit_function(out, &fn);
 }
 
@@ -88,7 +88,7 @@ codegen_linux_aarch64_emit_function(FILE *out, ast_fn_definition_t *fn)
     assert(literal_u32.kind == AST_LITERAL_U32);
     uint32_t exit_code = literal_u32.as_u32;
 
-    fprintf(out, "" SV_FMT ":\n", SV_ARG(fn->identifier));
+    fprintf(out, "" SV_FMT ":\n", SV_ARG(fn->id));
     fprintf(out, "    mov x0, #%d\n", exit_code);
     fprintf(out, "    ret\n");
 }
diff --git a/src/codegen_linux_x86_64.c b/src/codegen_linux_x86_64.c
index 2e329ca..a0d9d97 100644
--- a/src/codegen_linux_x86_64.c
+++ b/src/codegen_linux_x86_64.c
@@ -65,7 +65,7 @@ codegen_linux_x86_64_emit_program(codegen_x86_64_t *codegen, ast_node_t *node)
 
     ast_fn_definition_t fn = program.fn->as_fn_def;
 
-    assert(string_view_eq_to_cstr(fn.identifier, "main"));
+    assert(string_view_eq_to_cstr(fn.id, "main"));
     codegen_linux_x86_64_emit_function(codegen, &fn);
 }
 
@@ -103,7 +103,7 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
         case AST_NODE_REF: {
             ast_ref_t ref = expr_node->as_ref;
 
-            symbol_t *symbol = scope_lookup(ref.scope, ref.identifier);
+            symbol_t *symbol = scope_lookup(ref.scope, ref.id);
             assert(symbol);
 
             char symbol_ptr[PTR_HEX_CSTR_SIZE];
@@ -366,7 +366,7 @@ codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
                 ast_var_definition_t var_def = node->as_var_def;
                 scope_t *scope = var_def.scope;
 
-                symbol_t *symbol = scope_lookup(scope, var_def.identifier);
+                symbol_t *symbol = scope_lookup(scope, var_def.id);
                 assert(symbol);
 
                 char symbol_ptr[PTR_HEX_CSTR_SIZE];
@@ -486,7 +486,7 @@ codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen, ast_fn_definition_
     codegen->base_offset = X86_CALL_EIP_STACK_OFFSET;
 
     ast_node_t *block_node = fn->block;
-    fprintf(codegen->out, "" SV_FMT ":\n", SV_ARG(fn->identifier));
+    fprintf(codegen->out, "" SV_FMT ":\n", SV_ARG(fn->id));
 
     fprintf(codegen->out, "    mov %%rsp, %%rbp\n");
 
diff --git a/src/lexer.c b/src/lexer.c
index 12b4719..ebc21b7 100644
--- a/src/lexer.c
+++ b/src/lexer.c
@@ -278,7 +278,7 @@ lexer_next_token(lexer_t *lexer, token_t *token)
 
 static char *token_kind_str_table[] = {
     [TOKEN_UNKNOWN] = "unknown",
-    [TOKEN_IDENTIFIER] = "identifier",
+    [TOKEN_ID] = "identifier",
     [TOKEN_NUMBER] = "number",
     [TOKEN_FN] = "fn",
     [TOKEN_RETURN] = "return",
@@ -432,7 +432,7 @@ lexer_str_to_token_kind(string_view_t text)
         return TOKEN_FN;
     }
 
-    return TOKEN_IDENTIFIER;
+    return TOKEN_ID;
 }
 
 void
diff --git a/src/lexer.h b/src/lexer.h
index 4a0e2e1..717d21d 100644
--- a/src/lexer.h
+++ b/src/lexer.h
@@ -32,7 +32,7 @@ typedef struct lexer
 typedef enum token_kind
 {
     TOKEN_UNKNOWN,
-    TOKEN_IDENTIFIER,
+    TOKEN_ID,
     TOKEN_NUMBER,
 
     // Keywords
diff --git a/src/parser.c b/src/parser.c
index a63a724..3cae763 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -236,7 +236,7 @@ parser_parse_factor(parser_t *parser)
         case TOKEN_NUMBER:
             return ast_new_node_literal_u32(parser->arena, string_view_to_u32(token.value));
 
-        case TOKEN_IDENTIFIER:
+        case TOKEN_ID:
             return ast_new_node_ref(parser->arena, token.value);
 
         case TOKEN_OPAREN: {
@@ -269,7 +269,7 @@ parser_parse_fn_definition(parser_t *parser)
 
     token_t fn_name_token;
 
-    if (!expected_next_token(parser, &fn_name_token, TOKEN_IDENTIFIER)) {
+    if (!expected_next_token(parser, &fn_name_token, TOKEN_ID)) {
         return NULL;
     }
 
@@ -313,7 +313,7 @@ parser_parse_type(parser_t *parser, string_view_t *type)
 
     token_t token;
 
-    if (!expected_next_token(parser, &token, TOKEN_IDENTIFIER)) {
+    if (!expected_next_token(parser, &token, TOKEN_ID)) {
         return false;
     }
 
@@ -459,8 +459,8 @@ parser_parse_var_def(parser_t *parser)
         return NULL;
     }
 
-    token_t identifier_token;
-    if (!expected_next_token(parser, &identifier_token, TOKEN_IDENTIFIER)) {
+    token_t id_token;
+    if (!expected_next_token(parser, &id_token, TOKEN_ID)) {
         return NULL;
     }
 
@@ -479,7 +479,7 @@ parser_parse_var_def(parser_t *parser)
         return NULL;
     }
 
-    ast_node_t *var_node = ast_new_node_var_def(parser->arena, identifier_token.value, var_type, expr);
+    ast_node_t *var_node = ast_new_node_var_def(parser->arena, id_token.value, var_type, expr);
 
     skip_line_feeds(parser->lexer);
 
diff --git a/src/pretty_print_ast.c b/src/pretty_print_ast.c
index a7c75e8..1d5576d 100644
--- a/src/pretty_print_ast.c
+++ b/src/pretty_print_ast.c
@@ -127,7 +127,7 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
             char name[256];
             sprintf(name,
                     "Function_Definition <name:" SV_FMT "> <return:" SV_FMT ">",
-                    SV_ARG(fn_def.identifier),
+                    SV_ARG(fn_def.id),
                     SV_ARG(fn_def.return_type));
             node->name = (char *)arena_alloc(arena, sizeof(char) * (strlen(name) + 1));
             strcpy(node->name, name);
@@ -203,7 +203,7 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
             ast_var_definition_t var = ast->as_var_def;
 
             char name[256];
-            sprintf(name, "Var_Definition <name:" SV_FMT "> <kind:u32>", SV_ARG(var.identifier));
+            sprintf(name, "Var_Definition <name:" SV_FMT "> <kind:u32>", SV_ARG(var.id));
             node->name = (char *)arena_alloc(arena, sizeof(char) * (strlen(name) + 1));
             strcpy(node->name, name);
 
@@ -217,7 +217,7 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
             ast_ref_t ref = ast->as_ref;
 
             char name[256];
-            sprintf(name, "Reference <name:" SV_FMT ">", SV_ARG(ref.identifier));
+            sprintf(name, "Reference <name:" SV_FMT ">", SV_ARG(ref.id));
             node->name = (char *)arena_alloc(arena, sizeof(char) * (strlen(name) + 1));
             strcpy(node->name, name);
 
diff --git a/tests/unit/parser_test.c b/tests/unit/parser_test.c
index ccec460..8ad16b5 100644
--- a/tests/unit/parser_test.c
+++ b/tests/unit/parser_test.c
@@ -50,7 +50,7 @@ parse_program_test(const MunitParameter params[], void *user_data_or_fixture)
     assert_uint(program.fn->kind, ==, AST_NODE_FN_DEF);
 
     ast_fn_definition_t fn = program.fn->as_fn_def;
-    assert_memory_equal(fn.identifier.size, fn.identifier.chars, "main");
+    assert_memory_equal(fn.id.size, fn.id.chars, "main");
     assert_memory_equal(fn.return_type.size, fn.return_type.chars, "u32");
 
     ast_node_t *block = fn.block;
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v2 4/4] codegen: operate mov instructions based on the symbol's type
@ 2024-09-22  0:46 Carlos Maniero
  2024-09-22  0:47 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-09-22  0:46 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 src/codegen_linux_x86_64.c                    | 60 ++++++++++++++++---
 src/type.c                                    | 18 ++++++
 src/type.h                                    |  5 +-
 .../tests/0026_primitive_unsigneds.ol         | 27 +++++++++
 4 files changed, 100 insertions(+), 10 deletions(-)
 create mode 100644 tests/integration/tests/0026_primitive_unsigneds.ol

diff --git a/src/codegen_linux_x86_64.c b/src/codegen_linux_x86_64.c
index 1fa6c58..25cda2d 100644
--- a/src/codegen_linux_x86_64.c
+++ b/src/codegen_linux_x86_64.c
@@ -26,6 +26,10 @@
 #define SYS_exit (60)
 #define PTR_HEX_CSTR_SIZE (18 + 1)
 
+// The call instruction pushes EIP into stack so the first 8 bytes from stack
+// must be preserved else the ret instruction will jump to nowere.
+#define X86_CALL_EIP_STACK_OFFSET (8)
+
 // FIXME: move label_index to codegen_linux_x86_64_t structure
 size_t label_index;
 
@@ -35,6 +39,12 @@ codegen_linux_x86_64_emit_start_entrypoint(codegen_x86_64_t *codegen);
 static void
 codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen, ast_fn_definition_t *fn);
 
+static size_t
+type_to_bytes(type_t *type);
+
+static char *
+get_accumulator_reg_for(size_t bytes);
+
 void
 codegen_linux_x86_64_init(codegen_x86_64_t *codegen, arena_t *arena, FILE *out)
 {
@@ -105,7 +115,10 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
             size_t *offset = (size_t *)map_get(codegen->symbols_stack_offset, symbol_ptr);
             assert(offset);
 
-            fprintf(codegen->out, "    mov -%ld(%%rbp), %%rax\n", *offset);
+            fprintf(codegen->out,
+                    "    mov -%ld(%%rbp), %s\n",
+                    *offset,
+                    get_accumulator_reg_for(type_to_bytes(&symbol->type)));
             return;
         }
         case AST_NODE_BINARY_OP: {
@@ -366,12 +379,18 @@ codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
                     codegen_linux_x86_64_emit_expression(codegen, var_def.value);
                 }
 
-                codegen->base_offset += 8;
                 size_t *offset = arena_alloc(codegen->arena, sizeof(size_t));
                 *offset = codegen->base_offset;
 
                 map_put(codegen->symbols_stack_offset, symbol_ptr, offset);
-                fprintf(codegen->out, "    mov %%rax, -%ld(%%rbp)\n", codegen->base_offset);
+
+                size_t type_size = type_to_bytes(&symbol->type);
+
+                fprintf(codegen->out,
+                        "    mov %s, -%ld(%%rbp)\n",
+                        get_accumulator_reg_for(type_size),
+                        codegen->base_offset);
+                codegen->base_offset += type_size;
 
                 break;
             }
@@ -419,6 +438,18 @@ codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
     codegen->base_offset = block_offset;
 }
 
+static size_t
+type_to_bytes(type_t *type)
+{
+    switch (type->kind) {
+        case TYPE_PRIMITIVE: {
+            return type->as_primitive.size;
+        }
+    }
+
+    assert(0 && "unreachable");
+}
+
 static size_t
 calculate_fn_local_size(scope_t *scope)
 {
@@ -431,9 +462,8 @@ calculate_fn_local_size(scope_t *scope)
     map_get_kvs(scope->symbols, kvs);
 
     for (size_t i = 0; i < scope->symbols->size; ++i) {
-        // FIXME: symbols must have their types. Since we just have 8bytes
-        //        variables it is hard coded.
-        local_size += 8;
+        symbol_t *symbol = (symbol_t *)kvs[i]->value;
+        local_size += type_to_bytes(&symbol->type);
     }
 
     size_t max_child_local_size = 0;
@@ -456,7 +486,8 @@ calculate_fn_local_size(scope_t *scope)
 static void
 codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen, ast_fn_definition_t *fn)
 {
-    codegen->base_offset = 0;
+    codegen->base_offset = X86_CALL_EIP_STACK_OFFSET;
+
     ast_node_t *block_node = fn->block;
     fprintf(codegen->out, "" SV_FMT ":\n", SV_ARG(fn->identifier));
 
@@ -464,8 +495,6 @@ codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen, ast_fn_definition_
 
     size_t local_size = calculate_fn_local_size(fn->scope);
 
-    // TODO: get the local_size from function scope
-
     if (local_size != 0) {
         fprintf(codegen->out, "    sub $%ld, %%rsp\n", local_size);
     }
@@ -475,3 +504,16 @@ codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen, ast_fn_definition_
 
     codegen_linux_x86_64_emit_block(codegen, &block);
 }
+
+static char *
+get_accumulator_reg_for(size_t bytes)
+{
+    if (bytes <= 1) {
+        return "%ah";
+    } else if (bytes <= 2) {
+        return "%ax";
+    } else if (bytes <= 4) {
+        return "%eax";
+    }
+    return "%rax";
+}
diff --git a/src/type.c b/src/type.c
index cbdfbde..64147a2 100644
--- a/src/type.c
+++ b/src/type.c
@@ -21,12 +21,30 @@ type_t
 type_from_id(string_view_t id)
 {
     type_t type = { 0 };
+    if (string_view_eq_to_cstr(id, "u8")) {
+        type.kind = TYPE_PRIMITIVE;
+        type.as_primitive.size = 1;
+        type.as_primitive.kind = TYPE_U8;
+        return type;
+    }
+    if (string_view_eq_to_cstr(id, "u16")) {
+        type.kind = TYPE_PRIMITIVE;
+        type.as_primitive.size = 2;
+        type.as_primitive.kind = TYPE_U16;
+        return type;
+    }
     if (string_view_eq_to_cstr(id, "u32")) {
         type.kind = TYPE_PRIMITIVE;
         type.as_primitive.size = 4;
         type.as_primitive.kind = TYPE_U32;
         return type;
     }
+    if (string_view_eq_to_cstr(id, "u64")) {
+        type.kind = TYPE_PRIMITIVE;
+        type.as_primitive.size = 8;
+        type.as_primitive.kind = TYPE_U64;
+        return type;
+    }
 
     // FIXME: handle user defined types
     assert(0 && "unknown type");
diff --git a/src/type.h b/src/type.h
index b431171..1da3a11 100644
--- a/src/type.h
+++ b/src/type.h
@@ -24,7 +24,10 @@ typedef enum
 
 typedef enum
 {
-    TYPE_U32
+    TYPE_U8,
+    TYPE_U16,
+    TYPE_U32,
+    TYPE_U64
 } type_primitive_kind_t;
 
 typedef struct type_primitive
diff --git a/tests/integration/tests/0026_primitive_unsigneds.ol b/tests/integration/tests/0026_primitive_unsigneds.ol
new file mode 100644
index 0000000..25f0f7e
--- /dev/null
+++ b/tests/integration/tests/0026_primitive_unsigneds.ol
@@ -0,0 +1,27 @@
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+fn main(): u64 {
+  var a: u8 = 255
+  var b: u16 = 65535
+  var c: u32 = 4294967295
+  var d: u64 = 4294967296
+
+  return a + b + c + d - a - b - c - d
+}
+
+# TEST test_compile(exit_code=0)
+
+# TEST test_run_binary(exit_code=0)
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v1 2/2] tests: build: add parallelization support for unit tests
@ 2024-09-21 21:02 Johnny Richard
  2024-09-21 21:05 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-09-21 21:02 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Removes inline shell script and enable test execution by target on make.

Now we should be able to execute tests with more jobs (make -j<n-of-jobs>)

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 Makefile            |  4 ++--
 tests/unit/Makefile | 40 ++++++++++++++++++----------------------
 2 files changed, 20 insertions(+), 24 deletions(-)

diff --git a/Makefile b/Makefile
index 2b65f96..83c88a2 100644
--- a/Makefile
+++ b/Makefile
@@ -94,8 +94,8 @@ check-execute:
 	$(MAKE)
 	$(MAKE) -C tests/execute/
 
-.PHONY: unit-test
-unit-test:
+.PHONY: check-unit
+check-unit:
 	$(MAKE)
 	$(MAKE) -C tests/unit/
 
diff --git a/tests/unit/Makefile b/tests/unit/Makefile
index 783225c..686938f 100644
--- a/tests/unit/Makefile
+++ b/tests/unit/Makefile
@@ -1,33 +1,29 @@
-SRCS         := $(wildcard *_test.c)
-OBJS         := $(patsubst %_test.c, %_test.o, $(SRCS))
-SUBJECT_OBJS := $(filter-out ../../build/main.o, $(wildcard ../../build/*.o))
-CFLAGS       := -I../../src -I../shared
-TESTS        := $(patsubst %_test.c, %_test, $(SRCS))
-EXEC_TESTS   := $(patsubst %_test, ./%_test, $(TESTS))
-MUNIT_SRC    := ../shared/munit.c
-MUNIT        := ./munit.o
+SRCS := $(wildcard *.c)
+DEP_OBJS := $(filter-out ../../build/main.o, $(wildcard ../../build/*.o))
+CFLAGS := -I../../src -I../shared
+TESTS := $(patsubst %.c, %.bin, $(SRCS))
+RUN_TESTS := $(patsubst %.bin, %.run, $(TESTS))
+MUNIT_SRC := ../shared/munit.c
+MUNIT := ./munit.o
 
-.PHONY: all
-all: $(MUNIT) $(TESTS)
-	@for file in $(EXEC_TESTS); do \
-                ./"$$file"; \
-        done
+.PHONY: all clean format format-fix
+all: $(RUN_TESTS)
+
+%.bin: %.c $(MUNIT)
+	@$(CC) $(CFLAGS) $(MUNIT) $(DEP_OBJS) $< -o $@
+
+%.run: %.bin
+	@./$<
 
-.PHONY: clean
 clean:
-	$(RM) *.o *_test
-	$(RM) -rfv lib
+	@$(RM) *.o *.bin
+	@$(RM) -rfv lib
 
-.PHONY: format
 format: $(SRCS)
 	clang-format --dry-run --Werror $?
 
-.PHONY: format-fix
 format-fix: $(SRCS)
 	clang-format -i $?
 
-%_test: $(MUNIT) $(SUBJECT_OBJS) %_test.c
-	$(CC) $? $(CFLAGS) -o $@
-
 $(MUNIT):
-	$(CC) -c $(MUNIT_SRC) $(CFLAGS) -o $(MUNIT)
+	@$(CC) -c $(MUNIT_SRC) $(CFLAGS) -o $(MUNIT)
-- 
2.46.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang 5/5] codegen: perform mov instructions based on variable type
@ 2024-09-21  8:25 Carlos Maniero
  2024-09-21  8:26 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-09-21  8:25 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

There are two function that was introduced to performe the translations
bellow:

type | type_to_bytes | bytes_to_rax
-----------------------------------
  u8 | 1             | ah
 u16 | 2             | ax
 u32 | 4             | eax
 u64 | 8             | rax

I opted to create *bytes_to_rax* instead of *type_to_rax* mainly because
we may use the same function to mov literals once we extend the literals
as well (We say that all literals are u32 but we actually handle them as
u64 on codegen).

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 src/checker.c                                 | 15 +++++
 src/codegen_linux_x86_64.c                    | 64 +++++++++++++++++--
 src/type.h                                    |  5 +-
 .../tests/0026_primitive_unsigneds.ol         | 27 ++++++++
 4 files changed, 105 insertions(+), 6 deletions(-)
 create mode 100644 tests/integration/tests/0026_primitive_unsigneds.ol

diff --git a/src/checker.c b/src/checker.c
index f5068e0..3a78a59 100644
--- a/src/checker.c
+++ b/src/checker.c
@@ -133,11 +133,26 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
 static void
 evaluate_type(type_t *type)
 {
+    if (string_view_eq_to_cstr(type->id, "u8")) {
+        type->kind = TYPE_PRIMITIVE;
+        type->as_primitive = TYPE_U8;
+        return;
+    }
+    if (string_view_eq_to_cstr(type->id, "u16")) {
+        type->kind = TYPE_PRIMITIVE;
+        type->as_primitive = TYPE_U16;
+        return;
+    }
     if (string_view_eq_to_cstr(type->id, "u32")) {
         type->kind = TYPE_PRIMITIVE;
         type->as_primitive = TYPE_U32;
         return;
     }
+    if (string_view_eq_to_cstr(type->id, "u64")) {
+        type->kind = TYPE_PRIMITIVE;
+        type->as_primitive = TYPE_U64;
+        return;
+    }
 
     type->kind = TYPE_USER_DEFINED;
 }
diff --git a/src/codegen_linux_x86_64.c b/src/codegen_linux_x86_64.c
index 415c81b..fa2a082 100644
--- a/src/codegen_linux_x86_64.c
+++ b/src/codegen_linux_x86_64.c
@@ -38,6 +38,12 @@ codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen, ast_fn_definition_
 static size_t
 type_to_bytes(type_t *type);
 
+static char *
+bytes_to_mov(size_t bytes);
+
+static char *
+bytes_to_rax(size_t bytes);
+
 void
 codegen_linux_x86_64_init(codegen_x86_64_t *codegen, arena_t *arena, FILE *out)
 {
@@ -108,7 +114,10 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
             size_t *offset = (size_t *)map_get(codegen->symbols_stack_offset, symbol_ptr);
             assert(offset);
 
-            fprintf(codegen->out, "    mov -%ld(%%rbp), %%rax\n", *offset);
+            size_t type_size = type_to_bytes(&symbol->type);
+
+            fprintf(
+                codegen->out, "    %s -%ld(%%rbp), %s\n", bytes_to_mov(type_size), *offset, bytes_to_rax(type_size));
             return;
         }
         case AST_NODE_BINARY_OP: {
@@ -369,12 +378,19 @@ codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
                     codegen_linux_x86_64_emit_expression(codegen, var_def.value);
                 }
 
-                codegen->base_offset += type_to_bytes(&symbol->type);
                 size_t *offset = arena_alloc(codegen->arena, sizeof(size_t));
                 *offset = codegen->base_offset;
 
                 map_put(codegen->symbols_stack_offset, symbol_ptr, offset);
-                fprintf(codegen->out, "    mov %%rax, -%ld(%%rbp)\n", codegen->base_offset);
+
+                size_t type_size = type_to_bytes(&symbol->type);
+
+                fprintf(codegen->out,
+                        "    %s %s, -%ld(%%rbp)\n",
+                        bytes_to_mov(type_size),
+                        bytes_to_rax(type_size),
+                        codegen->base_offset);
+                codegen->base_offset += type_size;
 
                 break;
             }
@@ -427,7 +443,19 @@ type_to_bytes(type_t *type)
 {
     switch (type->kind) {
         case TYPE_PRIMITIVE: {
-            return 8;
+            switch (type->as_primitive) {
+                case TYPE_U8:
+                    return 1;
+                case TYPE_U16:
+                    return 2;
+                case TYPE_U32:
+                    return 4;
+                case TYPE_U64:
+                    return 8;
+            }
+
+            assert(0 && "unreachable");
+            return 0;
         }
         case TYPE_USER_DEFINED: {
             assert(0 && "user defined types are not defined yet");
@@ -474,7 +502,7 @@ calculate_fn_local_size(scope_t *scope)
 static void
 codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen, ast_fn_definition_t *fn)
 {
-    codegen->base_offset = 0;
+    codegen->base_offset = 8;
     ast_node_t *block_node = fn->block;
     fprintf(codegen->out, "" SV_FMT ":\n", SV_ARG(fn->identifier));
 
@@ -493,3 +521,29 @@ codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen, ast_fn_definition_
 
     codegen_linux_x86_64_emit_block(codegen, &block);
 }
+
+static char *
+bytes_to_mov(size_t bytes)
+{
+    if (bytes <= 1) {
+        return "movb";
+    } else if (bytes <= 2) {
+        return "movw";
+    } else if (bytes <= 4) {
+        return "movl";
+    }
+    return "movq";
+}
+
+static char *
+bytes_to_rax(size_t bytes)
+{
+    if (bytes <= 1) {
+        return "%ah";
+    } else if (bytes <= 2) {
+        return "%ax";
+    } else if (bytes <= 4) {
+        return "%eax";
+    }
+    return "%rax";
+}
diff --git a/src/type.h b/src/type.h
index 855cd83..32da9c0 100644
--- a/src/type.h
+++ b/src/type.h
@@ -24,7 +24,10 @@ typedef enum
 
 typedef enum
 {
-    TYPE_U32
+    TYPE_U8,
+    TYPE_U16,
+    TYPE_U32,
+    TYPE_U64
 } type_primitive_t;
 
 typedef struct type
diff --git a/tests/integration/tests/0026_primitive_unsigneds.ol b/tests/integration/tests/0026_primitive_unsigneds.ol
new file mode 100644
index 0000000..25f0f7e
--- /dev/null
+++ b/tests/integration/tests/0026_primitive_unsigneds.ol
@@ -0,0 +1,27 @@
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+fn main(): u64 {
+  var a: u8 = 255
+  var b: u16 = 65535
+  var c: u32 = 4294967295
+  var d: u64 = 4294967296
+
+  return a + b + c + d - a - b - c - d
+}
+
+# TEST test_compile(exit_code=0)
+
+# TEST test_run_binary(exit_code=0)
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang 5/5] codegen: preserve function's variable stack location
@ 2024-09-21  1:13 Carlos Maniero
  2024-09-21  1:13 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-09-21  1:13 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero, Johnny Richard

Since the x86 expressions uses stack instructions (push/pop), a prologue
is required to ensure the space reserved for variables are not modified
by these instructions.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
Co-authored-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/codegen_linux_x86_64.c                    | 43 +++++++++++++++++++
 .../tests/0025_var_definition_nested.ol       | 35 +++++++++++++++
 2 files changed, 78 insertions(+)
 create mode 100644 tests/integration/tests/0025_var_definition_nested.ol

diff --git a/src/codegen_linux_x86_64.c b/src/codegen_linux_x86_64.c
index 640c9fb..3ce11a7 100644
--- a/src/codegen_linux_x86_64.c
+++ b/src/codegen_linux_x86_64.c
@@ -346,6 +346,7 @@ codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
 
                 codegen_linux_x86_64_emit_expression(codegen, expr);
 
+                fprintf(codegen->out, "    mov %%rbp, %%rsp\n");
                 fprintf(codegen->out, "    ret\n");
 
                 break;
@@ -418,6 +419,40 @@ codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
     codegen->base_offset = block_offset;
 }
 
+static size_t
+calculate_fn_local_size(scope_t *scope)
+{
+    assert(scope);
+
+    size_t local_size = 0;
+
+    map_kv_t kvs[scope->symbols->size];
+
+    map_get_kvs(scope->symbols, (map_kv_t **)kvs);
+
+    for (size_t i = 0; i < scope->symbols->size; ++i) {
+        // FIXME: symbols must have their types. Since we just have 8bytes
+        //        variables it is hard coded.
+        local_size += 8;
+    }
+
+    size_t max_child_local_size = 0;
+
+    list_item_t *item = list_head(scope->children);
+
+    while (item != NULL) {
+        size_t child_local_size = calculate_fn_local_size((scope_t *)item->value);
+
+        if (child_local_size > max_child_local_size) {
+            max_child_local_size = child_local_size;
+        }
+
+        item = list_next(item);
+    }
+
+    return local_size + max_child_local_size;
+}
+
 static void
 codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen, ast_fn_definition_t *fn)
 {
@@ -427,6 +462,14 @@ codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen, ast_fn_definition_
 
     fprintf(codegen->out, "    mov %%rsp, %%rbp\n");
 
+    size_t local_size = calculate_fn_local_size(fn->scope);
+
+    // TODO: get the local_size from function scope
+
+    if (local_size != 0) {
+        fprintf(codegen->out, "    sub $%ld, %%rsp\n", local_size);
+    }
+
     assert(block_node->kind == AST_NODE_BLOCK);
     ast_block_t block = block_node->as_block;
 
diff --git a/tests/integration/tests/0025_var_definition_nested.ol b/tests/integration/tests/0025_var_definition_nested.ol
new file mode 100644
index 0000000..fdbe903
--- /dev/null
+++ b/tests/integration/tests/0025_var_definition_nested.ol
@@ -0,0 +1,35 @@
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+fn main(): u32 {
+  var a: u32 = 1
+
+  if a == 1 {
+    var b: u32 = 43
+    var c: u32 = 2
+
+    return a + b - c
+  } else {
+    var b: u32 = 41
+
+    return a + b
+  }
+
+  return a
+}
+
+# TEST test_compile(exit_code=0)
+
+# TEST test_run_binary(exit_code=42)
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v1 3/3] codegen: add support scopes and symbols lookups for var
@ 2024-09-21  0:20 Johnny Richard
  2024-09-21  0:23 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-09-21  0:20 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

It introduces a new struct designed to maintain the stack offset for
local variables. This data structure facilitates the sharing of state
between function calls, including essential information such as the FILE
*out pointer.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/codegen_linux_x86_64.c                    | 347 ++++++++++--------
 src/codegen_linux_x86_64.h                    |  16 +-
 src/main.c                                    |   8 +-
 .../integration/tests/0024_var_definition.ol  |   4 +
 4 files changed, 229 insertions(+), 146 deletions(-)

diff --git a/src/codegen_linux_x86_64.c b/src/codegen_linux_x86_64.c
index 8ed127c..4e0ea52 100644
--- a/src/codegen_linux_x86_64.c
+++ b/src/codegen_linux_x86_64.c
@@ -20,22 +20,38 @@
 
 #include "codegen_linux_x86_64.h"
 #include "list.h"
+#include "map.h"
+#include "scope.h"
 
 #define SYS_exit (60)
+#define PTR_HEX_CSTR_SIZE (18 + 1)
 
+// FIXME: move label_index to codegen_linux_x86_64_t structure
 size_t label_index;
 
 static void
-codegen_linux_x86_64_emit_start_entrypoint(FILE *out);
+codegen_linux_x86_64_emit_start_entrypoint(codegen_x86_64_t *codegen);
 
 static void
-codegen_linux_x86_64_emit_function(FILE *out, ast_fn_definition_t *fn);
+codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen, ast_fn_definition_t *fn);
 
 void
-codegen_linux_x86_64_emit_program(FILE *out, ast_node_t *node)
+codegen_linux_x86_64_init(codegen_x86_64_t *codegen, arena_t *arena, FILE *out)
+{
+    assert(codegen);
+    assert(arena);
+    assert(codegen);
+    codegen->base_offset = 0;
+    codegen->symbols_stack_offset = map_new(arena);
+    codegen->out = out;
+    codegen->arena = arena;
+}
+
+void
+codegen_linux_x86_64_emit_program(codegen_x86_64_t *codegen, ast_node_t *node)
 {
     label_index = 0;
-    codegen_linux_x86_64_emit_start_entrypoint(out);
+    codegen_linux_x86_64_emit_start_entrypoint(codegen);
 
     assert(node->kind == AST_NODE_PROGRAM);
     ast_program_t program = node->as_program;
@@ -43,20 +59,20 @@ codegen_linux_x86_64_emit_program(FILE *out, ast_node_t *node)
     ast_fn_definition_t fn = program.fn->as_fn_def;
 
     assert(string_view_eq_to_cstr(fn.identifier, "main"));
-    codegen_linux_x86_64_emit_function(out, &fn);
+    codegen_linux_x86_64_emit_function(codegen, &fn);
 }
 
 static void
-codegen_linux_x86_64_emit_start_entrypoint(FILE *out)
+codegen_linux_x86_64_emit_start_entrypoint(codegen_x86_64_t *codegen)
 {
-    fprintf(out, ".text\n");
-    fprintf(out, ".globl _start\n\n");
-
-    fprintf(out, "_start:\n");
-    fprintf(out, "    call main\n");
-    fprintf(out, "    mov %%eax, %%edi\n");
-    fprintf(out, "    mov $%d, %%eax\n", SYS_exit);
-    fprintf(out, "    syscall\n");
+    fprintf(codegen->out, ".text\n");
+    fprintf(codegen->out, ".globl _start\n\n");
+
+    fprintf(codegen->out, "_start:\n");
+    fprintf(codegen->out, "    call main\n");
+    fprintf(codegen->out, "    mov %%eax, %%edi\n");
+    fprintf(codegen->out, "    mov $%d, %%eax\n", SYS_exit);
+    fprintf(codegen->out, "    syscall\n");
 }
 
 static size_t
@@ -66,7 +82,7 @@ codegen_linux_x86_64_get_next_label(void)
 }
 
 static void
-codegen_linux_x86_64_emit_expression(FILE *out, ast_node_t *expr_node)
+codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr_node)
 {
     switch (expr_node->kind) {
         case AST_NODE_LITERAL: {
@@ -74,200 +90,215 @@ codegen_linux_x86_64_emit_expression(FILE *out, ast_node_t *expr_node)
             assert(literal_u32.kind == AST_LITERAL_U32);
             uint32_t n = literal_u32.as_u32;
 
-            fprintf(out, "    mov $%d, %%rax\n", n);
+            fprintf(codegen->out, "    mov $%d, %%rax\n", n);
+            return;
+        }
+        case AST_NODE_REF: {
+            ast_ref_t ref = expr_node->as_ref;
+
+            symbol_t *symbol = scope_lookup(ref.scope, ref.identifier);
+            assert(symbol);
+
+            char symbol_ptr[PTR_HEX_CSTR_SIZE];
+            sprintf(symbol_ptr, "%p", (void *)symbol);
+
+            size_t *offset = (size_t *)map_get(codegen->symbols_stack_offset, symbol_ptr);
+            assert(offset);
+
+            fprintf(codegen->out, "    mov -%ld(%%rbp), %%rax\n", *offset);
             return;
         }
         case AST_NODE_BINARY_OP: {
             ast_binary_op_t bin_op = expr_node->as_bin_op;
             switch (bin_op.kind) {
                 case AST_BINOP_ADDITION: {
-                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
-                    fprintf(out, "    push %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
-                    fprintf(out, "    pop %%rcx\n");
-                    fprintf(out, "    add %%rcx, %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    pop %%rcx\n");
+                    fprintf(codegen->out, "    add %%rcx, %%rax\n");
 
                     return;
                 }
                 case AST_BINOP_MULTIPLICATION: {
-                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
-                    fprintf(out, "    push %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
-                    fprintf(out, "    pop %%rcx\n");
-                    fprintf(out, "    mul %%rcx\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    pop %%rcx\n");
+                    fprintf(codegen->out, "    mul %%rcx\n");
 
                     return;
                 }
                 case AST_BINOP_DIVISION: {
-                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
-                    fprintf(out, "    push %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
-                    fprintf(out, "    pop %%rcx\n");
-                    fprintf(out, "    xor %%rdx, %%rdx\n");
-                    fprintf(out, "    div %%rcx\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    pop %%rcx\n");
+                    fprintf(codegen->out, "    xor %%rdx, %%rdx\n");
+                    fprintf(codegen->out, "    div %%rcx\n");
 
                     return;
                 }
                 case AST_BINOP_REMINDER: {
-                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
-                    fprintf(out, "    push %%rax\n");
-                    fprintf(out, "    xor %%edx, %%edx\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    push %%rax\n");
+                    fprintf(codegen->out, "    xor %%edx, %%edx\n");
 
-                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
-                    fprintf(out, "    pop %%rcx\n");
-                    fprintf(out, "    xor %%rdx, %%rdx\n");
-                    fprintf(out, "    div %%rcx\n");
-                    fprintf(out, "    mov %%edx, %%eax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    pop %%rcx\n");
+                    fprintf(codegen->out, "    xor %%rdx, %%rdx\n");
+                    fprintf(codegen->out, "    div %%rcx\n");
+                    fprintf(codegen->out, "    mov %%edx, %%eax\n");
 
                     return;
                 }
                 case AST_BINOP_SUBTRACTION: {
-                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
-                    fprintf(out, "    push %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
-                    fprintf(out, "    pop %%rcx\n");
-                    fprintf(out, "    sub %%rcx, %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    pop %%rcx\n");
+                    fprintf(codegen->out, "    sub %%rcx, %%rax\n");
 
                     return;
                 }
                 case AST_BINOP_CMP_EQ: {
-                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
-                    fprintf(out, "    push %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
-                    fprintf(out, "    pop %%rcx\n");
-                    fprintf(out, "    cmp %%rcx, %%rax\n");
-                    fprintf(out, "    sete %%al\n");
-                    fprintf(out, "    movzb %%al, %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    pop %%rcx\n");
+                    fprintf(codegen->out, "    cmp %%rcx, %%rax\n");
+                    fprintf(codegen->out, "    sete %%al\n");
+                    fprintf(codegen->out, "    movzb %%al, %%rax\n");
 
                     return;
                 }
                 case AST_BINOP_CMP_LT: {
-                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
-                    fprintf(out, "    push %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
-                    fprintf(out, "    pop %%rcx\n");
-                    fprintf(out, "    cmp %%rcx, %%rax\n");
-                    fprintf(out, "    setl %%al\n");
-                    fprintf(out, "    movzb %%al, %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    pop %%rcx\n");
+                    fprintf(codegen->out, "    cmp %%rcx, %%rax\n");
+                    fprintf(codegen->out, "    setl %%al\n");
+                    fprintf(codegen->out, "    movzb %%al, %%rax\n");
 
                     return;
                 }
                 case AST_BINOP_CMP_GT: {
-                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
-                    fprintf(out, "    push %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
-                    fprintf(out, "    pop %%rcx\n");
-                    fprintf(out, "    cmp %%rcx, %%rax\n");
-                    fprintf(out, "    setg %%al\n");
-                    fprintf(out, "    movzb %%al, %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    pop %%rcx\n");
+                    fprintf(codegen->out, "    cmp %%rcx, %%rax\n");
+                    fprintf(codegen->out, "    setg %%al\n");
+                    fprintf(codegen->out, "    movzb %%al, %%rax\n");
 
                     return;
                 }
                 case AST_BINOP_CMP_NEQ: {
-                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
-                    fprintf(out, "    push %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
-                    fprintf(out, "    pop %%rcx\n");
-                    fprintf(out, "    cmp %%rcx, %%rax\n");
-                    fprintf(out, "    setne %%al\n");
-                    fprintf(out, "    movzb %%al, %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    pop %%rcx\n");
+                    fprintf(codegen->out, "    cmp %%rcx, %%rax\n");
+                    fprintf(codegen->out, "    setne %%al\n");
+                    fprintf(codegen->out, "    movzb %%al, %%rax\n");
 
                     return;
                 }
                 case AST_BINOP_CMP_LEQ: {
-                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
-                    fprintf(out, "    push %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
-                    fprintf(out, "    pop %%rcx\n");
-                    fprintf(out, "    cmp %%rcx, %%rax\n");
-                    fprintf(out, "    setle %%al\n");
-                    fprintf(out, "    movzb %%al, %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    pop %%rcx\n");
+                    fprintf(codegen->out, "    cmp %%rcx, %%rax\n");
+                    fprintf(codegen->out, "    setle %%al\n");
+                    fprintf(codegen->out, "    movzb %%al, %%rax\n");
 
                     return;
                 }
                 case AST_BINOP_CMP_GEQ: {
-                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
-                    fprintf(out, "    push %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
-                    fprintf(out, "    pop %%rcx\n");
-                    fprintf(out, "    cmp %%rcx, %%rax\n");
-                    fprintf(out, "    setge %%al\n");
-                    fprintf(out, "    movzb %%al, %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    pop %%rcx\n");
+                    fprintf(codegen->out, "    cmp %%rcx, %%rax\n");
+                    fprintf(codegen->out, "    setge %%al\n");
+                    fprintf(codegen->out, "    movzb %%al, %%rax\n");
 
                     return;
                 }
                 case AST_BINOP_BITWISE_LSHIFT: {
-                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
-                    fprintf(out, "    push %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
-                    fprintf(out, "    pop %%rcx\n");
-                    fprintf(out, "    shl %%cl, %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    pop %%rcx\n");
+                    fprintf(codegen->out, "    shl %%cl, %%rax\n");
 
                     return;
                 }
                 case AST_BINOP_BITWISE_RSHIFT: {
-                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
-                    fprintf(out, "    push %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
-                    fprintf(out, "    pop %%rcx\n");
-                    fprintf(out, "    shr %%cl, %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    pop %%rcx\n");
+                    fprintf(codegen->out, "    shr %%cl, %%rax\n");
 
                     return;
                 }
                 case AST_BINOP_BITWISE_XOR: {
-                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
-                    fprintf(out, "    push %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
-                    fprintf(out, "    pop %%rcx\n");
-                    fprintf(out, "    xor %%ecx, %%eax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    pop %%rcx\n");
+                    fprintf(codegen->out, "    xor %%ecx, %%eax\n");
 
                     return;
                 }
                 case AST_BINOP_BITWISE_AND: {
-                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
-                    fprintf(out, "    push %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
-                    fprintf(out, "    pop %%rcx\n");
-                    fprintf(out, "    and %%ecx, %%eax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    pop %%rcx\n");
+                    fprintf(codegen->out, "    and %%ecx, %%eax\n");
 
                     return;
                 }
                 case AST_BINOP_BITWISE_OR: {
-                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
-                    fprintf(out, "    push %%rax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
-                    fprintf(out, "    pop %%rcx\n");
-                    fprintf(out, "    or %%ecx, %%eax\n");
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    pop %%rcx\n");
+                    fprintf(codegen->out, "    or %%ecx, %%eax\n");
 
                     return;
                 }
                 case AST_BINOP_LOGICAL_AND: {
                     size_t label_exit = codegen_linux_x86_64_get_next_label();
 
-                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
-                    fprintf(out, "    cmp $0, %%rax\n");
-                    fprintf(out, "    je .L%ld\n", label_exit);
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    cmp $0, %%rax\n");
+                    fprintf(codegen->out, "    je .L%ld\n", label_exit);
 
-                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
-                    fprintf(out, "    cmp $0, %%rax\n");
-                    fprintf(out, "    je .L%ld\n", label_exit);
-                    fprintf(out, "    mov $1, %%rax\n");
-                    fprintf(out, ".L%ld:\n", label_exit);
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    cmp $0, %%rax\n");
+                    fprintf(codegen->out, "    je .L%ld\n", label_exit);
+                    fprintf(codegen->out, "    mov $1, %%rax\n");
+                    fprintf(codegen->out, ".L%ld:\n", label_exit);
 
                     return;
                 }
@@ -275,17 +306,17 @@ codegen_linux_x86_64_emit_expression(FILE *out, ast_node_t *expr_node)
                     size_t label_t = codegen_linux_x86_64_get_next_label();
                     size_t label_f = codegen_linux_x86_64_get_next_label();
 
-                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
-                    fprintf(out, "    cmp $0, %%rax\n");
-                    fprintf(out, "    jne .L%ld\n", label_t);
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    cmp $0, %%rax\n");
+                    fprintf(codegen->out, "    jne .L%ld\n", label_t);
 
-                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
-                    fprintf(out, "    cmp $0, %%rax\n");
-                    fprintf(out, "    je .L%ld\n", label_f);
+                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    cmp $0, %%rax\n");
+                    fprintf(codegen->out, "    je .L%ld\n", label_f);
 
-                    fprintf(out, ".L%ld:\n", label_t);
-                    fprintf(out, "    mov $1, %%rax\n");
-                    fprintf(out, ".L%ld:\n", label_f);
+                    fprintf(codegen->out, ".L%ld:\n", label_t);
+                    fprintf(codegen->out, "    mov $1, %%rax\n");
+                    fprintf(codegen->out, ".L%ld:\n", label_f);
 
                     return;
                 }
@@ -300,7 +331,7 @@ codegen_linux_x86_64_emit_expression(FILE *out, ast_node_t *expr_node)
     }
 }
 static void
-codegen_linux_x86_64_emit_block(FILE *out, ast_block_t *block)
+codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
 {
 
     size_t nodes_len = list_size(block->nodes);
@@ -313,12 +344,37 @@ codegen_linux_x86_64_emit_block(FILE *out, ast_block_t *block)
 
                 ast_node_t *expr = return_stmt.data;
 
-                codegen_linux_x86_64_emit_expression(out, expr);
+                codegen_linux_x86_64_emit_expression(codegen, expr);
+
+                fprintf(codegen->out, "    ret\n");
 
-                fprintf(out, "    ret\n");
+                break;
+            }
+
+            case AST_NODE_VAR_DEF: {
+                ast_var_definition_t var_def = node->as_var_def;
+                scope_t *scope = var_def.scope;
+
+                symbol_t *symbol = scope_lookup(scope, var_def.identifier);
+                assert(symbol);
+
+                char symbol_ptr[PTR_HEX_CSTR_SIZE];
+                sprintf(symbol_ptr, "%p", (void *)symbol);
+
+                if (var_def.value) {
+                    codegen_linux_x86_64_emit_expression(codegen, var_def.value);
+                }
+
+                codegen->base_offset += 8;
+                size_t *offset = arena_alloc(codegen->arena, sizeof(size_t));
+                *offset = codegen->base_offset;
+
+                map_put(codegen->symbols_stack_offset, symbol_ptr, offset);
+                fprintf(codegen->out, "    mov %%rax, -%ld(%%rbp)\n", codegen->base_offset);
 
                 break;
             }
+
             case AST_NODE_IF_STMT: {
                 ast_if_stmt_t if_stmt = node->as_if_stmt;
 
@@ -329,28 +385,30 @@ codegen_linux_x86_64_emit_block(FILE *out, ast_block_t *block)
                 size_t end_if_label = codegen_linux_x86_64_get_next_label();
                 size_t end_else_label = codegen_linux_x86_64_get_next_label();
 
-                codegen_linux_x86_64_emit_expression(out, cond);
-                fprintf(out, "    cmp $1, %%rax\n");
-                fprintf(out, "    jnz .L%ld\n", end_if_label);
+                codegen_linux_x86_64_emit_expression(codegen, cond);
+                fprintf(codegen->out, "    cmp $1, %%rax\n");
+                fprintf(codegen->out, "    jnz .L%ld\n", end_if_label);
 
                 assert(then->kind == AST_NODE_BLOCK && "invalid if-then block");
                 ast_block_t then_block = then->as_block;
 
-                codegen_linux_x86_64_emit_block(out, &then_block);
-                fprintf(out, "    jmp .L%ld\n", end_else_label);
+                codegen_linux_x86_64_emit_block(codegen, &then_block);
+                fprintf(codegen->out, "    jmp .L%ld\n", end_else_label);
 
-                fprintf(out, ".L%ld:\n", end_if_label);
+                fprintf(codegen->out, ".L%ld:\n", end_if_label);
 
                 if (_else != NULL) {
                     ast_block_t else_block = _else->as_block;
-                    codegen_linux_x86_64_emit_block(out, &else_block);
+                    codegen_linux_x86_64_emit_block(codegen, &else_block);
                 }
 
-                fprintf(out, ".L%ld:\n", end_else_label);
+                fprintf(codegen->out, ".L%ld:\n", end_else_label);
 
                 break;
             }
             default: {
+                // FIXME: improve error: replace the node->kind to a string representation
+                fprintf(stderr, "node kind %d not supported\n", node->kind);
                 assert(0 && "unsupported block statement");
                 break;
             }
@@ -359,13 +417,16 @@ codegen_linux_x86_64_emit_block(FILE *out, ast_block_t *block)
 }
 
 static void
-codegen_linux_x86_64_emit_function(FILE *out, ast_fn_definition_t *fn)
+codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen, ast_fn_definition_t *fn)
 {
+    codegen->base_offset = 0;
     ast_node_t *block_node = fn->block;
-    fprintf(out, "" SV_FMT ":\n", SV_ARG(fn->identifier));
+    fprintf(codegen->out, "" SV_FMT ":\n", SV_ARG(fn->identifier));
+
+    fprintf(codegen->out, "    mov %%rsp, %%rbp\n");
 
     assert(block_node->kind == AST_NODE_BLOCK);
     ast_block_t block = block_node->as_block;
 
-    codegen_linux_x86_64_emit_block(out, &block);
+    codegen_linux_x86_64_emit_block(codegen, &block);
 }
diff --git a/src/codegen_linux_x86_64.h b/src/codegen_linux_x86_64.h
index 2050c91..cad530a 100644
--- a/src/codegen_linux_x86_64.h
+++ b/src/codegen_linux_x86_64.h
@@ -17,9 +17,23 @@
 #ifndef CODEGEN_X86_64_H
 #define CODEGEN_X86_64_H
 
+#include "arena.h"
 #include "ast.h"
+#include "map.h"
+#include <stdio.h>
+
+typedef struct codegen_x86_64
+{
+    arena_t *arena;
+    size_t base_offset;
+    map_t *symbols_stack_offset;
+    FILE *out;
+} codegen_x86_64_t;
+
+void
+codegen_linux_x86_64_init(codegen_x86_64_t *codegen, arena_t *arena, FILE *out);
 
 void
-codegen_linux_x86_64_emit_program(FILE *out, ast_node_t *prog);
+codegen_linux_x86_64_emit_program(codegen_x86_64_t *codegen, ast_node_t *prog);
 
 #endif /* CODEGEN_X86_64_H */
diff --git a/src/main.c b/src/main.c
index 810bf2d..312b0c1 100644
--- a/src/main.c
+++ b/src/main.c
@@ -147,10 +147,14 @@ handle_codegen_linux(cli_opts_t *opts)
     assert(out);
 
     if (!(opts->options & CLI_OPT_ARCH)) {
-        codegen_linux_x86_64_emit_program(out, ast);
+        codegen_x86_64_t codegen = { 0 };
+        codegen_linux_x86_64_init(&codegen, &arena, out);
+        codegen_linux_x86_64_emit_program(&codegen, ast);
     } else {
         if (strcmp(opts->arch, "x86_64") == 0) {
-            codegen_linux_x86_64_emit_program(out, ast);
+            codegen_x86_64_t codegen = { 0 };
+            codegen_linux_x86_64_init(&codegen, &arena, out);
+            codegen_linux_x86_64_emit_program(&codegen, ast);
         } else if (strcmp(opts->arch, "aarch64") == 0) {
             codegen_linux_aarch64_emit_program(out, ast);
         } else {
diff --git a/tests/integration/tests/0024_var_definition.ol b/tests/integration/tests/0024_var_definition.ol
index 241fc1a..5c23449 100644
--- a/tests/integration/tests/0024_var_definition.ol
+++ b/tests/integration/tests/0024_var_definition.ol
@@ -18,6 +18,10 @@ fn main(): u32 {
   return code
 }
 
+# TEST test_compile(exit_code=0)
+
+# TEST test_run_binary(exit_code=0)
+
 # TEST test_contains_tokens WITH
 # ./tests/0024_var_definition.ol:17:3: <var>
 # END
-- 
2.46.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang] cli: add libc error handling
@ 2024-09-17 15:14 Carlos Maniero
  2024-09-17 15:15 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-09-17 15:14 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

The application was ignoring libc functions result and not handling
possible errors. This effort also makes the application POSIX complaint.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 src/main.c | 22 +++++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/src/main.c b/src/main.c
index e16695b..030f34c 100644
--- a/src/main.c
+++ b/src/main.c
@@ -164,14 +164,24 @@ handle_codegen_linux(cli_opts_t *opts)
 
     char command[512];
     sprintf(command, "%s/bin/as %s -o " SV_FMT ".o", opts->sysroot, asm_file, SV_ARG(opts->output_bin));
-    system(command);
+
+    int exit_code = system(command);
+
+    if (exit_code != 0) {
+        exit(exit_code);
+    }
 
     sprintf(command,
             "%s/bin/ld " SV_FMT ".o -o " SV_FMT "",
             opts->sysroot,
             SV_ARG(opts->output_bin),
             SV_ARG(opts->output_bin));
-    system(command);
+
+    exit_code = system(command);
+
+    if (exit_code != 0) {
+        exit(exit_code);
+    }
 
     if (!(opts->options & CLI_OPT_SAVE_TEMPS)) {
         char output_file[256];
@@ -211,7 +221,13 @@ read_entire_file(char *file_path, arena_t *arena)
         exit(EXIT_FAILURE);
     }
 
-    fread(file_content.chars, 1, file_content.size, stream);
+    size_t read_bytes = fread(file_content.chars, 1, file_content.size, stream);
+
+    if (read_bytes != file_content.size) {
+        fprintf(stderr, "error: failed to read all file bytes %s\n", file_path);
+        exit(EXIT_FAILURE);
+    }
+
     fclose(stream);
 
     return file_content;

base-commit: 5802359caf011c960b455dcda7ce10ea794b0ea1
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v1] remove unused examples programs
@ 2024-09-17 13:43 Johnny Richard
  2024-09-17 11:43 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-09-17 13:43 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

These examples were used to test the compiler previously, after the new
way of testing the compiler these files are not necessary anymore.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 examples/expression.ol | 3 ---
 examples/main_exit.ol  | 6 ------
 2 files changed, 9 deletions(-)
 delete mode 100644 examples/expression.ol
 delete mode 100644 examples/main_exit.ol

diff --git a/examples/expression.ol b/examples/expression.ol
deleted file mode 100644
index efa4ab5..0000000
--- a/examples/expression.ol
+++ /dev/null
@@ -1,3 +0,0 @@
-fn main(): u32 {
-  return (10 + 1 * 2) - (10 - (1 + 1) / 2)
-}
diff --git a/examples/main_exit.ol b/examples/main_exit.ol
deleted file mode 100644
index 8952017..0000000
--- a/examples/main_exit.ol
+++ /dev/null
@@ -1,6 +0,0 @@
-# Expected:
-# - output: "" 
-
-fn main(): u32 {
-  return 0
-}

base-commit: 5a9ba5e6b37739787f631d9775c187405a69b084
-- 
2.46.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v1 4/4] docs: info: add instructions to install/uninstall olang
@ 2024-09-17 12:46 Johnny Richard
  2024-09-17 10:48 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-09-17 12:46 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 docs/info/installation.texi | 78 +++++++++++++++++++++++++++++++++++++
 1 file changed, 78 insertions(+)

diff --git a/docs/info/installation.texi b/docs/info/installation.texi
index ef79184..d61b0e9 100644
--- a/docs/info/installation.texi
+++ b/docs/info/installation.texi
@@ -1,2 +1,80 @@
 @node Installation
 @chapter Installation
+
+This installation assumes you are running an unix like operation system and
+requires @code{make}, @code{makeinfo} and a @strong{c compiler} installed.
+
+The following commands will compile the the code and install @code{olang}
+binary, @code{man} pages and @code{info} docs into your system.
+
+@verbatim
+$ make
+$ make install
+@end verbatim
+
+And for uninstall the program run
+
+@verbatim
+$ make uninstall
+@end verbatim
+
+@section Custom Installation
+
+There are few @code{make} variables which can be used to customize your
+installation as described bellow.
+
+@table @samp
+
+@item PREFIX
+
+The prefix where the compiler and docs should be installed, it is set to
+@code{/usr/local} if not specified.
+
+@item BINDIR
+
+The path where the compiler binary will be installed, it is set to
+@code{$PREFIX/bin} if not specified.
+
+@item DATADIR
+
+The path where the read-only documents will be installed, it is set to
+@code{$PREFIX/share} if not specified.
+
+@item MANDIR
+
+The path where the man documents will be installed, it is set to
+@code{$DATADIR/man} if not specified.
+
+@item MAN1DIR
+
+The path where the man1 documents will be installed, it is set to
+@code{$MANDIR/man1} if not specified.
+
+@item INFODIR
+
+The path where the info documents will be installed, it is set to
+@code{$DATADIR/info} if not specified.
+
+@item DESTDIR
+
+In case you are installing the compiler on different root than @code{/} (by
+default the install assumes the destination dir as root), you can set the
+variable @strong{DESTDIR} on @code{make} for example:
+
+@verbatim
+$ make DESTDIR=/mnt/linux-root install
+@end verbatim
+
+@end table
+
+@section Developer Tips
+
+If you want to install the compiler but do not want to make it system
+available, you might want to have it installed on the @code{XDG}
+@strong{~/.local} directory.  Make sure you have set @code{PATH},
+@code{MANPATH} and @code{INFOPATH} correctly so you can have the resources
+installed available on your shell.
+
+@verbatim
+$ make PREFIX=~/.local install
+@end verbatim
-- 
2.46.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v1 3/3] docs: remove pandoc dependency for man docs
@ 2024-09-16 16:29 Johnny Richard
  2024-09-16 14:31 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-09-16 16:29 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Pandoc is an excelente tool and works pretty well, but I think it's
overkill to keep it just for create a manpage.  The man pages should be
pretty simple to create using the roff syntax so lets keep things
simpler with less dependency.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 .build.yml             |  1 -
 docs/Makefile          |  4 ++--
 docs/manpages/olang.1  | 53 ++++++++++++++++++++++++++++++++++++++++++
 docs/manpages/olang.md | 40 -------------------------------
 4 files changed, 55 insertions(+), 43 deletions(-)
 create mode 100644 docs/manpages/olang.1
 delete mode 100644 docs/manpages/olang.md

diff --git a/.build.yml b/.build.yml
index 3cbba82..8265da2 100644
--- a/.build.yml
+++ b/.build.yml
@@ -5,7 +5,6 @@ packages:
   - make
   - hut
   - clang
-  - pandoc-cli
   - texinfo
 environment:
   site: o-lang.org
diff --git a/docs/Makefile b/docs/Makefile
index 3d01e0f..a38c091 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -26,8 +26,8 @@ manpages: $(BUILD_DIR) $(MANPAGES)/olang.1
 manual: $(SITE_DIR)/manual/index.html
 
 
-$(MANPAGES)/%.1: manpages/%.md
-	$(PANDOC) -s -t man $< > $@
+$(MANPAGES)/%.1: manpages/%.1
+	@cp $< $@
 
 $(DIST_FILE): all
 	tar -czf $(DIST_FILE) -C $(SITE_DIR) .
diff --git a/docs/manpages/olang.1 b/docs/manpages/olang.1
new file mode 100644
index 0000000..fed1e4e
--- /dev/null
+++ b/docs/manpages/olang.1
@@ -0,0 +1,53 @@
+.\" Man page for olang
+.\" Contact ~johnnyrichard/olang-devel@lists.sr.ht to report issues
+
+.TH man 1 "Feb 2024" "0.0" "olang man page"
+
+.SH NAME
+
+olang \- O programming language compiler
+
+.SH SYNOPSIS
+
+olang source_file
+
+[ --dump-tokens ] [ --dump-ast ] [ [ -o output_file [ --save-temps ] [ --arch arch ]  [ --sysroot dir] ]
+
+.SH DESCRIPTION
+
+.B olang
+is the official O programming language compiler, it is also a tool that contains
+utilities to help the language development.
+
+.SH OPTIONS
+
+.TP
+.BI \-\-dump-tokens
+Display lexical tokens given a soruce.ol code.
+
+.TP
+.BR \-\-dump-ast
+Display AST tree to stdout right after syntax analyzes
+
+.TP
+.BI \-o\  file
+Compile program into a binary file
+
+.TP
+.BR \-\-save\-temps
+Keep temp files used to compile program
+
+.TP
+.BI \-\-arch\  arch
+
+Binary arch: default to "x86_64", avaliable options ("x86_64" | "aarch64")
+
+.TP
+.BI \-\-sysroot\  dir
+
+System root dir where the GNU Assembler and GNU Linker are located: default to '/'
+
+
+.SH AUTHOR
+
+Olang Maintainers <~johnnyrichard/olang-devel@lists.sr.ht>
diff --git a/docs/manpages/olang.md b/docs/manpages/olang.md
deleted file mode 100644
index fbca5c3..0000000
--- a/docs/manpages/olang.md
+++ /dev/null
@@ -1,40 +0,0 @@
-% OLANG(1)
-% olang mantainers
-% Feb 2024
-
-# NAME
-
-olang - O Programming Language compiler
-
-# SYNOPSIS
-
-**olang**
-    source_file
-    [**----dump-tokens**]
-    [**----dump-ast**]
-    [**--o** ___output_file___ [**----save-temps**] [**----arch** ___arch___] [**----sysroot** ___dir___]]
-
-# DESCRIPTION
-
-**olang** is the offical O programming language compiler, it is also a tool that
-contains utilities to help the language development.
-
-# GENERAL OPTIONS
-
-**----dump-tokens**
-:   Display lexical tokens given a soruce.0 code.
-
-**----dump-ast**
-:   Display AST tree to stdout right after syntax analyzes
-
-**--o** ___file___
-:   Compile program into a binary file
-
-**----save-temps**
-:   Keep temp files used to compile program
-
-**----arch** ___architecture___
-:   Binary arch: default to "x86_64", avaliable options ("x86_64" | "aarch64")
-
-**----sysroot**  ___dir___
-:   System root dir where the GNU Assembler and GNU Linker are located: default to '/'
-- 
2.46.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v1 2/2] parser: add var definition and reference support
@ 2024-09-11  1:03 Johnny Richard
  2024-09-10 23:05 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-09-11  1:03 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

In order to get the var definition parsed we also introduce the
reference node to get a functional ast test.

This first implementation assumes that all variables will be of type u32
and we don't allow variable definitions without assignment, this
assignment issue will be addressed in the future.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/ast.c                                     | 28 ++++++++++
 src/ast.h                                     | 22 ++++++++
 src/parser.c                                  | 52 +++++++++++++++++++
 src/pretty_print_ast.c                        | 25 +++++++++
 .../integration/tests/0024_var_definition.ol  | 14 ++++-
 5 files changed, 139 insertions(+), 2 deletions(-)

diff --git a/src/ast.c b/src/ast.c
index 4b252ae..d2fd2fa 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -52,6 +52,22 @@ ast_new_node_fn_def(arena_t *arena, string_view_t identifier, type_t return_type
     return node_fn_def;
 }
 
+ast_node_t *
+ast_new_node_var_def(arena_t *arena, string_view_t identifier, type_t type, ast_node_t *value)
+{
+    ast_node_t *node_var_def = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
+    assert(node_var_def);
+
+    node_var_def->kind = AST_NODE_VAR_DEF;
+    ast_var_definition_t *var_def = &node_var_def->as_var_def;
+
+    var_def->identifier = identifier;
+    var_def->type = type;
+    var_def->value = value;
+
+    return node_var_def;
+}
+
 ast_node_t *
 ast_new_node_bin_op(arena_t *arena, ast_binary_op_kind_t kind, ast_node_t *lhs, ast_node_t *rhs)
 {
@@ -79,6 +95,18 @@ ast_new_node_literal_u32(arena_t *arena, uint32_t value)
     return node_literal;
 }
 
+ast_node_t *
+ast_new_node_ref(arena_t *arena, string_view_t identifier)
+{
+    ast_node_t *node_ref = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
+    assert(node_ref);
+
+    node_ref->kind = AST_NODE_REF;
+    node_ref->as_ref.identifier = identifier;
+
+    return node_ref;
+}
+
 ast_node_t *
 ast_new_node_return_stmt(arena_t *arena)
 {
diff --git a/src/ast.h b/src/ast.h
index 9bf3723..94ee6b4 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -30,10 +30,12 @@ typedef enum
     AST_NODE_PROGRAM,
     AST_NODE_BLOCK,
     AST_NODE_FN_DEF,
+    AST_NODE_VAR_DEF,
     AST_NODE_BINARY_OP,
     AST_NODE_RETURN_STMT,
     AST_NODE_IF_STMT,
     AST_NODE_LITERAL,
+    AST_NODE_REF,
     AST_NODE_UNKNOWN
 } ast_node_kind_t;
 
@@ -59,6 +61,13 @@ typedef struct ast_fn_definition
     ast_node_t *block;
 } ast_fn_definition_t;
 
+typedef struct ast_var_definition
+{
+    string_view_t identifier;
+    type_t type;
+    ast_node_t *value;
+} ast_var_definition_t;
+
 typedef enum
 {
     AST_LITERAL_U32
@@ -73,6 +82,11 @@ typedef struct ast_literal
     };
 } ast_literal_t;
 
+typedef struct ast_ref
+{
+    string_view_t identifier;
+} ast_ref_t;
+
 typedef enum ast_binary_op_kind
 {
     AST_BINOP_ADDITION,
@@ -121,8 +135,10 @@ typedef struct ast_node
     {
         ast_program_t as_program;
         ast_fn_definition_t as_fn_def;
+        ast_var_definition_t as_var_def;
         ast_binary_op_t as_bin_op;
         ast_literal_t as_literal;
+        ast_ref_t as_ref;
         ast_block_t as_block;
         ast_return_stmt_t as_return_stmt;
         ast_if_stmt_t as_if_stmt;
@@ -135,12 +151,18 @@ ast_new_program(arena_t *arena, ast_node_t *fn_def);
 ast_node_t *
 ast_new_node_fn_def(arena_t *arena, string_view_t identifier, type_t return_type, ast_node_t *block);
 
+ast_node_t *
+ast_new_node_var_def(arena_t *arena, string_view_t identifier, type_t type, ast_node_t *value);
+
 ast_node_t *
 ast_new_node_bin_op(arena_t *arena, ast_binary_op_kind_t kind, ast_node_t *lhs, ast_node_t *rhs);
 
 ast_node_t *
 ast_new_node_literal_u32(arena_t *arena, uint32_t value);
 
+ast_node_t *
+ast_new_node_ref(arena_t *arena, string_view_t identifier);
+
 ast_node_t *
 ast_new_node_return_stmt(arena_t *arena);
 
diff --git a/src/parser.c b/src/parser.c
index 8f49f5e..d71500f 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -45,6 +45,9 @@ parser_parse_return_stmt(parser_t *parser);
 static ast_node_t *
 parser_parse_if_stmt(parser_t *parser);
 
+static ast_node_t *
+parser_parse_var_def(parser_t *parser);
+
 ast_node_t *
 parser_parse_fn_definition(parser_t *parser);
 
@@ -233,6 +236,9 @@ parser_parse_factor(parser_t *parser)
         case TOKEN_NUMBER:
             return ast_new_node_literal_u32(parser->arena, string_view_to_u32(token.value));
 
+        case TOKEN_IDENTIFIER:
+            return ast_new_node_ref(parser->arena, token.value);
+
         case TOKEN_OPAREN: {
             ast_node_t *expr = parser_parse_expr(parser);
             if (expr == NULL) {
@@ -348,7 +354,15 @@ StartLoop:
             node = parser_parse_if_stmt(parser);
             break;
         }
+        case TOKEN_VAR: {
+            node = parser_parse_var_def(parser);
+            break;
+        }
+        case TOKEN_CCURLY: {
+            goto EndLoop;
+        }
         default: {
+            // FIXME: write a better error message
             goto EndLoop;
         }
     }
@@ -443,6 +457,44 @@ parser_parse_if_stmt(parser_t *parser)
     return node_if_stmt;
 }
 
+static ast_node_t *
+parser_parse_var_def(parser_t *parser)
+{
+    if (!skip_expected_token(parser, TOKEN_VAR)) {
+        return NULL;
+    }
+
+    token_t identifier_token;
+    if (!expected_next_token(parser, &identifier_token, TOKEN_IDENTIFIER)) {
+        return NULL;
+    }
+
+    if (!skip_expected_token(parser, TOKEN_COLON)) {
+        return NULL;
+    }
+
+    type_t var_type;
+    if (!parser_parse_type(parser, &var_type)) {
+        return NULL;
+    }
+
+    // FIXME: assignment must be optional according to the spec
+    if (!skip_expected_token(parser, TOKEN_EQ)) {
+        return NULL;
+    }
+
+    ast_node_t *expr = parser_parse_expr(parser);
+    if (expr == NULL) {
+        return NULL;
+    }
+
+    ast_node_t *var_node = ast_new_node_var_def(parser->arena, identifier_token.value, var_type, expr);
+
+    skip_line_feeds(parser->lexer);
+
+    return var_node;
+}
+
 static bool
 skip_expected_token(parser_t *parser, token_kind_t expected_kind)
 {
diff --git a/src/pretty_print_ast.c b/src/pretty_print_ast.c
index 3c12716..d6eef67 100644
--- a/src/pretty_print_ast.c
+++ b/src/pretty_print_ast.c
@@ -198,6 +198,31 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
 
             return node;
         }
+        case AST_NODE_VAR_DEF: {
+            pretty_print_node_t *node = pretty_print_node_new(arena);
+            ast_var_definition_t var = ast->as_var_def;
+
+            char name[256];
+            sprintf(name, "Var_Definition <name:" SV_FMT "> <kind:u32>", SV_ARG(var.identifier));
+            node->name = (char *)arena_alloc(arena, sizeof(char) * (strlen(name) + 1));
+            strcpy(node->name, name);
+
+            pretty_print_node_t *child = ast_node_to_pretty_print_node(var.value, arena);
+            list_append(node->children, child);
+
+            return node;
+        }
+        case AST_NODE_REF: {
+            pretty_print_node_t *node = pretty_print_node_new(arena);
+            ast_ref_t ref = ast->as_ref;
+
+            char name[256];
+            sprintf(name, "Reference <name:" SV_FMT ">", SV_ARG(ref.identifier));
+            node->name = (char *)arena_alloc(arena, sizeof(char) * (strlen(name) + 1));
+            strcpy(node->name, name);
+
+            return node;
+        }
         case AST_NODE_BINARY_OP: {
             pretty_print_node_t *node = pretty_print_node_new(arena);
             ast_binary_op_t binop = ast->as_bin_op;
diff --git a/tests/integration/tests/0024_var_definition.ol b/tests/integration/tests/0024_var_definition.ol
index d0b68da..241fc1a 100644
--- a/tests/integration/tests/0024_var_definition.ol
+++ b/tests/integration/tests/0024_var_definition.ol
@@ -14,10 +14,20 @@
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 fn main(): u32 {
-  var code: u32 = 0;
-  return code;
+  var code: u32 = 0
+  return code
 }
 
 # TEST test_contains_tokens WITH
 # ./tests/0024_var_definition.ol:17:3: <var>
 # END
+
+# TEST test_ast WITH
+# Translation_Unit
+# `-Function_Definition <name:main> <return:0>
+#   `-Block
+#     |-Var_Definition <name:code> <kind:u32>
+#     | `-Literal <kind:u32> <value:0>
+#     `-Return_Statement
+#       `-Reference <name:code>
+# END
-- 
2.46.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v2 2/2] codegen: x86_64: implement binary operations
@ 2024-08-25 13:16 Johnny Richard
  2024-08-25 13:26 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-08-25 13:16 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

In order to simplify most of the binary operation expressions we are
using stack to store previous execution.  It makes the implementation
easy but inefficient.

We can optimize this codegen in a near future as soon as re get IR in
place.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
CHANGES

- V2: add integration tests

 src/codegen_linux_x86_64.c                    | 234 +++++++++++++++++-
 src/parser.c                                  |   2 +-
 .../tests/0002_binary_operator_addition.ol    |  22 ++
 .../0003_binary_operator_multiplication.ol    |  22 ++
 .../tests/0004_binary_operator_division.ol    |  22 ++
 .../tests/0005_binary_operator_reminder.ol    |  22 ++
 .../tests/0006_binary_operator_subtraction.ol |  22 ++
 .../tests/0007_binary_operator_eq.ol          |  22 ++
 .../tests/0008_binary_operator_lt.ol          |  22 ++
 .../tests/0009_binary_operator_gt.ol          |  22 ++
 .../tests/0010_binary_operator_neq.ol         |  22 ++
 .../tests/0011_binary_operator_leq.ol         |  22 ++
 .../tests/0012_binary_operator_geq.ol         |  22 ++
 .../tests/0013_binary_operator_lshift.ol      |  22 ++
 .../tests/0014_binary_operator_rshift.ol      |  22 ++
 .../tests/0015_binary_operator_xor.ol         |  22 ++
 .../tests/0016_binary_operator_and.ol         |  22 ++
 .../tests/0017_binary_operator_or.ol          |  22 ++
 .../tests/0018_binary_operator_logical_and.ol |  22 ++
 .../tests/0019_binary_operator_logical_or.ol  |  22 ++
 20 files changed, 627 insertions(+), 5 deletions(-)
 create mode 100644 tests/integration/tests/0002_binary_operator_addition.ol
 create mode 100644 tests/integration/tests/0003_binary_operator_multiplication.ol
 create mode 100644 tests/integration/tests/0004_binary_operator_division.ol
 create mode 100644 tests/integration/tests/0005_binary_operator_reminder.ol
 create mode 100644 tests/integration/tests/0006_binary_operator_subtraction.ol
 create mode 100644 tests/integration/tests/0007_binary_operator_eq.ol
 create mode 100644 tests/integration/tests/0008_binary_operator_lt.ol
 create mode 100644 tests/integration/tests/0009_binary_operator_gt.ol
 create mode 100644 tests/integration/tests/0010_binary_operator_neq.ol
 create mode 100644 tests/integration/tests/0011_binary_operator_leq.ol
 create mode 100644 tests/integration/tests/0012_binary_operator_geq.ol
 create mode 100644 tests/integration/tests/0013_binary_operator_lshift.ol
 create mode 100644 tests/integration/tests/0014_binary_operator_rshift.ol
 create mode 100644 tests/integration/tests/0015_binary_operator_xor.ol
 create mode 100644 tests/integration/tests/0016_binary_operator_and.ol
 create mode 100644 tests/integration/tests/0017_binary_operator_or.ol
 create mode 100644 tests/integration/tests/0018_binary_operator_logical_and.ol
 create mode 100644 tests/integration/tests/0019_binary_operator_logical_or.ol

diff --git a/src/codegen_linux_x86_64.c b/src/codegen_linux_x86_64.c
index b277014..64ec0e0 100644
--- a/src/codegen_linux_x86_64.c
+++ b/src/codegen_linux_x86_64.c
@@ -23,6 +23,8 @@
 
 #define SYS_exit (60)
 
+size_t label_index;
+
 static void
 codegen_linux_x86_64_emit_start_entrypoint(FILE *out);
 
@@ -32,6 +34,7 @@ codegen_linux_x86_64_emit_function(FILE *out, ast_fn_definition_t *fn);
 void
 codegen_linux_x86_64_emit_program(FILE *out, ast_node_t *node)
 {
+    label_index = 0;
     codegen_linux_x86_64_emit_start_entrypoint(out);
 
     assert(node->kind == AST_NODE_PROGRAM);
@@ -56,6 +59,12 @@ codegen_linux_x86_64_emit_start_entrypoint(FILE *out)
     fprintf(out, "    syscall\n");
 }
 
+static size_t
+codegen_linux_x86_64_get_next_label(void)
+{
+    return ++label_index;
+}
+
 static void
 codegen_linux_x86_64_emit_expression(FILE *out, ast_node_t *expr_node)
 {
@@ -63,14 +72,231 @@ codegen_linux_x86_64_emit_expression(FILE *out, ast_node_t *expr_node)
         case AST_NODE_LITERAL: {
             ast_literal_t literal_u32 = expr_node->as_literal;
             assert(literal_u32.kind == AST_LITERAL_U32);
-            uint32_t exit_code = literal_u32.as_u32;
+            uint32_t n = literal_u32.as_u32;
 
-            fprintf(out, "    mov $%d, %%eax\n", exit_code);
+            fprintf(out, "    mov $%d, %%rax\n", n);
             return;
         }
-        case AST_NODE_BINARY_OP:
+        case AST_NODE_BINARY_OP: {
+            ast_binary_op_t bin_op = expr_node->as_bin_op;
+            switch (bin_op.kind) {
+                case AST_BINOP_ADDITION: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    add %%rcx, %%rax\n");
+
+                    return;
+                }
+                case AST_BINOP_MULTIPLICATION: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    mul %%rcx\n");
+
+                    return;
+                }
+                case AST_BINOP_DIVISION: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    xor %%rdx, %%rdx\n");
+                    fprintf(out, "    div %%rcx\n");
+
+                    return;
+                }
+                case AST_BINOP_REMINDER: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+                    fprintf(out, "    xor %%edx, %%edx\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    xor %%rdx, %%rdx\n");
+                    fprintf(out, "    div %%rcx\n");
+                    fprintf(out, "    mov %%edx, %%eax\n");
+
+                    return;
+                }
+                case AST_BINOP_SUBTRACTION: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    sub %%rcx, %%rax\n");
+
+                    return;
+                }
+                case AST_BINOP_CMP_EQ: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    cmp %%rcx, %%rax\n");
+                    fprintf(out, "    sete %%al\n");
+                    fprintf(out, "    movzb %%al, %%rax\n");
+
+                    return;
+                }
+                case AST_BINOP_CMP_LT: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    cmp %%rcx, %%rax\n");
+                    fprintf(out, "    setl %%al\n");
+                    fprintf(out, "    movzb %%al, %%rax\n");
+
+                    return;
+                }
+                case AST_BINOP_CMP_GT: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    cmp %%rcx, %%rax\n");
+                    fprintf(out, "    setg %%al\n");
+                    fprintf(out, "    movzb %%al, %%rax\n");
+
+                    return;
+                }
+                case AST_BINOP_CMP_NEQ: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    cmp %%rcx, %%rax\n");
+                    fprintf(out, "    setne %%al\n");
+                    fprintf(out, "    movzb %%al, %%rax\n");
+
+                    return;
+                }
+                case AST_BINOP_CMP_LEQ: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    cmp %%rcx, %%rax\n");
+                    fprintf(out, "    setle %%al\n");
+                    fprintf(out, "    movzb %%al, %%rax\n");
+
+                    return;
+                }
+                case AST_BINOP_CMP_GEQ: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    cmp %%rcx, %%rax\n");
+                    fprintf(out, "    setge %%al\n");
+                    fprintf(out, "    movzb %%al, %%rax\n");
+
+                    return;
+                }
+                case AST_BINOP_BITWISE_LSHIFT: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    shl %%cl, %%rax\n");
+
+                    return;
+                }
+                case AST_BINOP_BITWISE_RSHIFT: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    shr %%cl, %%rax\n");
+
+                    return;
+                }
+                case AST_BINOP_BITWISE_XOR: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    xor %%ecx, %%eax\n");
+
+                    return;
+                }
+                case AST_BINOP_BITWISE_AND: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    and %%ecx, %%eax\n");
+
+                    return;
+                }
+                case AST_BINOP_BITWISE_OR: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    or %%ecx, %%eax\n");
+
+                    return;
+                }
+                case AST_BINOP_LOGICAL_AND: {
+                    size_t label_exit = codegen_linux_x86_64_get_next_label();
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    cmp $0, %%rax\n");
+                    fprintf(out, "    je .L%ld\n", label_exit);
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    cmp $0, %%rax\n");
+                    fprintf(out, "    je .L%ld\n", label_exit);
+                    fprintf(out, "    mov $1, %%rax\n");
+                    fprintf(out, ".L%ld:\n", label_exit);
+
+                    return;
+                }
+                case AST_BINOP_LOGICAL_OR: {
+                    size_t label_t = codegen_linux_x86_64_get_next_label();
+                    size_t label_f = codegen_linux_x86_64_get_next_label();
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    cmp $0, %%rax\n");
+                    fprintf(out, "    jne .L%ld\n", label_t);
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    cmp $0, %%rax\n");
+                    fprintf(out, "    je .L%ld\n", label_f);
+
+                    fprintf(out, ".L%ld:\n", label_t);
+                    fprintf(out, "    mov $1, %%rax\n");
+                    fprintf(out, ".L%ld:\n", label_f);
+
+                    return;
+                }
+                default: {
+                    assert(0 && "unsupported binary operation");
+                    return;
+                }
+            }
+        }
         default:
-            assert(0 && "NOT IMPLEMENTED");
+            assert(0 && "unsupported expression");
     }
 }
 
diff --git a/src/parser.c b/src/parser.c
index eefca01..24094b3 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -152,7 +152,7 @@ get_binary_op_precedence(token_kind_t kind)
         case TOKEN_CMP_LEQ:
         case TOKEN_CMP_GEQ:
             return BINOP_CMP_RELATIONAL_PREC;
-        case TOKEN_EQ:
+        case TOKEN_CMP_EQ:
         case TOKEN_CMP_NEQ:
             return BINOP_CMP_EQUALITY_PREC;
         case TOKEN_AND:
diff --git a/tests/integration/tests/0002_binary_operator_addition.ol b/tests/integration/tests/0002_binary_operator_addition.ol
new file mode 100644
index 0000000..e408b47
--- /dev/null
+++ b/tests/integration/tests/0002_binary_operator_addition.ol
@@ -0,0 +1,22 @@
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# spec: Binary operator +
+fn main(): u32 {
+  return 10 + 11
+}
+
+# TEST test_compile(exit_code=0)
+# TEST test_run_binary(exit_code=21)
diff --git a/tests/integration/tests/0003_binary_operator_multiplication.ol b/tests/integration/tests/0003_binary_operator_multiplication.ol
new file mode 100644
index 0000000..8826d5d
--- /dev/null
+++ b/tests/integration/tests/0003_binary_operator_multiplication.ol
@@ -0,0 +1,22 @@
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# spec: Binary operator *
+fn main(): u32 {
+  return 10 * 11
+}
+
+# TEST test_compile(exit_code=0)
+# TEST test_run_binary(exit_code=110)
diff --git a/tests/integration/tests/0004_binary_operator_division.ol b/tests/integration/tests/0004_binary_operator_division.ol
new file mode 100644
index 0000000..0578fc7
--- /dev/null
+++ b/tests/integration/tests/0004_binary_operator_division.ol
@@ -0,0 +1,22 @@
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# spec: Binary operator /
+fn main(): u32 {
+  return 13 / 3 / 3
+}
+
+# TEST test_compile(exit_code=0)
+# TEST test_run_binary(exit_code=1)
diff --git a/tests/integration/tests/0005_binary_operator_reminder.ol b/tests/integration/tests/0005_binary_operator_reminder.ol
new file mode 100644
index 0000000..e4d0276
--- /dev/null
+++ b/tests/integration/tests/0005_binary_operator_reminder.ol
@@ -0,0 +1,22 @@
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# spec: Binary operator %
+fn main(): u32 {
+  return 13 % 3 % 3
+}
+
+# TEST test_compile(exit_code=0)
+# TEST test_run_binary(exit_code=1)
diff --git a/tests/integration/tests/0006_binary_operator_subtraction.ol b/tests/integration/tests/0006_binary_operator_subtraction.ol
new file mode 100644
index 0000000..cf16f4a
--- /dev/null
+++ b/tests/integration/tests/0006_binary_operator_subtraction.ol
@@ -0,0 +1,22 @@
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# spec: Binary operator -
+fn main(): u32 {
+  return 2024 - 1988
+}
+
+# TEST test_compile(exit_code=0)
+# TEST test_run_binary(exit_code=36)
diff --git a/tests/integration/tests/0007_binary_operator_eq.ol b/tests/integration/tests/0007_binary_operator_eq.ol
new file mode 100644
index 0000000..08dd243
--- /dev/null
+++ b/tests/integration/tests/0007_binary_operator_eq.ol
@@ -0,0 +1,22 @@
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# spec: Binary operator ==
+fn main(): u32 {
+  return 10 == 10 == 1 == 1
+}
+
+# TEST test_compile(exit_code=0)
+# TEST test_run_binary(exit_code=1)
diff --git a/tests/integration/tests/0008_binary_operator_lt.ol b/tests/integration/tests/0008_binary_operator_lt.ol
new file mode 100644
index 0000000..bd175fa
--- /dev/null
+++ b/tests/integration/tests/0008_binary_operator_lt.ol
@@ -0,0 +1,22 @@
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# spec: Binary operator <
+fn main(): u32 {
+  return 10 < 11
+}
+
+# TEST test_compile(exit_code=0)
+# TEST test_run_binary(exit_code=1)
diff --git a/tests/integration/tests/0009_binary_operator_gt.ol b/tests/integration/tests/0009_binary_operator_gt.ol
new file mode 100644
index 0000000..69d6cec
--- /dev/null
+++ b/tests/integration/tests/0009_binary_operator_gt.ol
@@ -0,0 +1,22 @@
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# spec: Binary operator >
+fn main(): u32 {
+  return 128 > 255
+}
+
+# TEST test_compile(exit_code=0)
+# TEST test_run_binary(exit_code=0)
diff --git a/tests/integration/tests/0010_binary_operator_neq.ol b/tests/integration/tests/0010_binary_operator_neq.ol
new file mode 100644
index 0000000..db4efe8
--- /dev/null
+++ b/tests/integration/tests/0010_binary_operator_neq.ol
@@ -0,0 +1,22 @@
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# spec: Binary operator !=
+fn main(): u32 {
+  return 10 != 20
+}
+
+# TEST test_compile(exit_code=0)
+# TEST test_run_binary(exit_code=1)
diff --git a/tests/integration/tests/0011_binary_operator_leq.ol b/tests/integration/tests/0011_binary_operator_leq.ol
new file mode 100644
index 0000000..63927ee
--- /dev/null
+++ b/tests/integration/tests/0011_binary_operator_leq.ol
@@ -0,0 +1,22 @@
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# spec: Binary operator <=
+fn main(): u32 {
+  return 10 <= 20
+}
+
+# TEST test_compile(exit_code=0)
+# TEST test_run_binary(exit_code=1)
diff --git a/tests/integration/tests/0012_binary_operator_geq.ol b/tests/integration/tests/0012_binary_operator_geq.ol
new file mode 100644
index 0000000..4737bc3
--- /dev/null
+++ b/tests/integration/tests/0012_binary_operator_geq.ol
@@ -0,0 +1,22 @@
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# spec: Binary operator >=
+fn main(): u32 {
+  return 10 >= 11
+}
+
+# TEST test_compile(exit_code=0)
+# TEST test_run_binary(exit_code=0)
diff --git a/tests/integration/tests/0013_binary_operator_lshift.ol b/tests/integration/tests/0013_binary_operator_lshift.ol
new file mode 100644
index 0000000..8239114
--- /dev/null
+++ b/tests/integration/tests/0013_binary_operator_lshift.ol
@@ -0,0 +1,22 @@
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# spec: Binary operator <<
+fn main(): u32 {
+  return 11 << 1
+}
+
+# TEST test_compile(exit_code=0)
+# TEST test_run_binary(exit_code=22)
diff --git a/tests/integration/tests/0014_binary_operator_rshift.ol b/tests/integration/tests/0014_binary_operator_rshift.ol
new file mode 100644
index 0000000..fc40e92
--- /dev/null
+++ b/tests/integration/tests/0014_binary_operator_rshift.ol
@@ -0,0 +1,22 @@
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# spec: Binary operator >>
+fn main(): u32 {
+  return 10 >> 1
+}
+
+# TEST test_compile(exit_code=0)
+# TEST test_run_binary(exit_code=5)
diff --git a/tests/integration/tests/0015_binary_operator_xor.ol b/tests/integration/tests/0015_binary_operator_xor.ol
new file mode 100644
index 0000000..c5c0568
--- /dev/null
+++ b/tests/integration/tests/0015_binary_operator_xor.ol
@@ -0,0 +1,22 @@
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# spec: Binary operator ^ 
+fn main(): u32 {
+  return 10 ^ 11
+}
+
+# TEST test_compile(exit_code=0)
+# TEST test_run_binary(exit_code=1)
diff --git a/tests/integration/tests/0016_binary_operator_and.ol b/tests/integration/tests/0016_binary_operator_and.ol
new file mode 100644
index 0000000..7a60ac3
--- /dev/null
+++ b/tests/integration/tests/0016_binary_operator_and.ol
@@ -0,0 +1,22 @@
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# spec: Binary operator &
+fn main(): u32 {
+  return 10 & 11
+}
+
+# TEST test_compile(exit_code=0)
+# TEST test_run_binary(exit_code=10)
diff --git a/tests/integration/tests/0017_binary_operator_or.ol b/tests/integration/tests/0017_binary_operator_or.ol
new file mode 100644
index 0000000..4c82b49
--- /dev/null
+++ b/tests/integration/tests/0017_binary_operator_or.ol
@@ -0,0 +1,22 @@
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# spec: Binary operator |
+fn main(): u32 {
+  return 10 | 11
+}
+
+# TEST test_compile(exit_code=0)
+# TEST test_run_binary(exit_code=11)
diff --git a/tests/integration/tests/0018_binary_operator_logical_and.ol b/tests/integration/tests/0018_binary_operator_logical_and.ol
new file mode 100644
index 0000000..aeb0885
--- /dev/null
+++ b/tests/integration/tests/0018_binary_operator_logical_and.ol
@@ -0,0 +1,22 @@
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# spec: Binary operator &&
+fn main(): u32 {
+  return 10 && 11
+}
+
+# TEST test_compile(exit_code=0)
+# TEST test_run_binary(exit_code=1)
diff --git a/tests/integration/tests/0019_binary_operator_logical_or.ol b/tests/integration/tests/0019_binary_operator_logical_or.ol
new file mode 100644
index 0000000..f889afe
--- /dev/null
+++ b/tests/integration/tests/0019_binary_operator_logical_or.ol
@@ -0,0 +1,22 @@
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# spec: Binary operator ||
+fn main(): u32 {
+  return 10 || 11
+}
+
+# TEST test_compile(exit_code=0)
+# TEST test_run_binary(exit_code=1)
-- 
2.46.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang 1/2] tests: add comment based integration tests mechanism
@ 2024-08-21  3:39 Carlos Maniero
  2024-08-21  3:41 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-08-21  3:39 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

To create an integration tests you just need to create a olang file at
*tests/integration/tests* directory. The assertions are performed
thought a couple of comments.

All tests features must start with the TEST statement. Arguments can be
provided to the test feature thought invocation using
*test_feature(arg_name_1=arg_value_1,arg_name_n=arg_value_n)* and
multi-line text (test contents) can be passed thought tests over the
WITH statement.

Available test features:
========================

test_compile
------------

Compiles the program, assert the exit code and the stdout/stderr if WITH
statement is present.

test_run_binary
---------------

Run the binary, assert the exit code and check the binary stdout/stderr
if WITH statement is present. The test_run_binary depends on the
test_compile.

test_ast
--------

Executes the *olang --dump-ast* over the test file and check if the ast
matches for the given test contents over the diff command.

test_contains_tokens
--------------------

Validate that every given token by the test contents is present on the
output of *olang --dump-tokens*. Since the language has the line_feed
token it is impossible to test the EOF once when added a comment the EOF
will be located at line + 1. So that the tokens test the first n-lines
defined. So instead of performing a diff like test_ast it validates line
by line.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 Makefile                                  |   3 -
 tests/integration/Makefile                |  34 +---
 tests/integration/test.sh                 | 231 ++++++++++++++++++++++
 tests/integration/tests/0001_main_exit.ol |  46 +++++
 4 files changed, 283 insertions(+), 31 deletions(-)
 create mode 100755 tests/integration/test.sh
 create mode 100644 tests/integration/tests/0001_main_exit.ol

diff --git a/Makefile b/Makefile
index 27337d4..2f5273b 100644
--- a/Makefile
+++ b/Makefile
@@ -19,13 +19,11 @@ $(BUILD_DIR):
 .PHONY: format
 format: $(SRCS) $(HEADERS)
 	clang-format --dry-run --Werror $?
-	$(MAKE) -C tests/integration/ format
 	$(MAKE) -C tests/unit/ format
 
 .PHONY: format-fix
 format-fix: $(SRCS) $(HEADERS)
 	clang-format -i $?
-	$(MAKE) -C tests/integration/ format-fix
 	$(MAKE) -C tests/unit/ format-fix
 
 .PHONY: integration-test
@@ -40,7 +38,6 @@ unit-test:
 
 .PHONY: clean
 clean:
-	$(MAKE) -C tests/integration/ clean
 	$(MAKE) -C tests/unit/ clean
 	@rm -rf build/ $(TARGET)
 
diff --git a/tests/integration/Makefile b/tests/integration/Makefile
index 4625707..bda51af 100644
--- a/tests/integration/Makefile
+++ b/tests/integration/Makefile
@@ -1,31 +1,9 @@
-SRCS        := $(wildcard *_test.c)
-OBJS        := $(patsubst %_test.c, %_test.o, $(SRCS))
-CFLAGS      := -I../../src -I../shared
-TESTS       := $(patsubst %_test.c, %_test, $(SRCS))
-EXEC_TESTS  := $(patsubst %_test, ./%_test, $(TESTS))
-MUNIT_SRC   := ../shared/munit.c
-MUNIT       := ./munit.o
+TESTER_SRC := ./test.sh
+TESTS := $(wildcard ./tests/*.ol)
 
 .PHONY: all
-all: $(MUNIT) proc_exec.o cli_runner.o $(TESTS)
-	@for file in $(EXEC_TESTS); do \
-                ./"$$file"; \
+all:
+	@set -e; \
+	for file in $(TESTS); do \
+                $(TESTER_SRC) "$$file"; \
         done
-
-.PHONY: clean
-clean:
-	$(RM) *.o *_test
-
-.PHONY: format
-format: $(SRCS)
-	clang-format --dry-run --Werror $?
-
-.PHONY: format-fix
-format-fix: $(SRCS)
-	clang-format -i $?
-
-cli_test: $(MUNIT) proc_exec.o cli_runner.o cli_test.o
-	$(CC) $? $(CFLAGS) -o $@
-
-$(MUNIT):
-	$(CC) -c $(MUNIT_SRC) $(CFLAGS) -o $(MUNIT)
diff --git a/tests/integration/test.sh b/tests/integration/test.sh
new file mode 100755
index 0000000..ae0a707
--- /dev/null
+++ b/tests/integration/test.sh
@@ -0,0 +1,231 @@
+#!/bin/sh
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# Ignores variables been modified in a subshell
+# shellcheck disable=SC2030,SC2031
+OLANG_PATH="../../olang"
+TEST_FILE="$1"
+
+TEST_TMP_FILES="$TEST_FILE.test"
+TEST_TMP_BIN="$TEST_TMP_FILES.bin"
+
+# Execution state vars
+TEST_NAME=""
+TEST_INVOCATION=""
+TEST_LINE_NUMBER=""
+TEST_CONTENTS_PATH=""
+TEST_ARGS=""
+TEST_SOME_PASSED=""
+
+# UI
+COLOR_RED=1
+COLOR_GREEN=2
+COLOR_YELLOW=3
+COLOR_CYAN=6
+COLOR_GRAY=7
+
+colored() {
+  text="$1"
+
+  if [ -t 1 ]; then
+    if tput setaf 1 > /dev/null 2>&1; then
+      color=$(tput setaf "$2")
+      reset=$(tput sgr0)
+
+      printf "%s%s%s" "$color" "$text" "$reset"
+      return
+    fi
+  fi
+
+  printf "%s" "$text"
+}
+# end UI
+
+# test output
+print_failed() {
+  reason="$1"
+
+  if [ -n "$TEST_SOME_PASSED" ]; then
+    echo
+  fi
+
+  colored "$TEST_FILE:$TEST_LINE_NUMBER:1: " $COLOR_GRAY 
+  colored "[$TEST_INVOCATION] " $COLOR_CYAN
+  colored "failed" $COLOR_RED
+  if [ -n "$reason" ]; then
+    printf ": %s" "$reason"
+  fi
+  echo
+}
+
+print_passed() {
+  colored "." $COLOR_GREEN
+  TEST_SOME_PASSED="true"
+}
+# end test output
+
+# expectations
+expect_output_contains() {
+  actual_file="$1"
+  expected_file="$2"
+  original_line_number="$TEST_LINE_NUMBER"
+
+  while read -r expected_line ; do
+    TEST_LINE_NUMBER=$((TEST_LINE_NUMBER+1))
+
+    if [ "$(grep "$(printf "%s" "$expected_line" | sed 's/\\/\\\\/g')" "$actual_file" | wc -c)" = "0" ]; then
+      print_failed
+      colored "(not found) $expected_line" $COLOR_YELLOW
+      echo
+      colored "$(awk '{print "(actual) " $0}' "$actual_file")" $COLOR_GRAY
+      echo
+      exit 1
+    fi
+  done < "$expected_file"
+
+  TEST_LINE_NUMBER="$original_line_number"
+}
+
+diff_output() {
+  actual_file="$1"
+  expected_file="$2"
+
+  if cmp -s "$expected_file" "$actual_file"; then
+    return
+  fi
+
+  print_failed "match failed"
+  diff "$actual_file" "$expected_file" -u --color
+  exit 1
+}
+
+cleanup() {
+  rm -rf "$TEST_TMP_FILES"*
+}
+
+main() {
+  all_test_end="$(grep -n "# END" "$TEST_FILE" | awk -F: '{print $1}')"
+
+  grep -n "# TEST " "$TEST_FILE" | while read -r line ; do
+    TEST_LINE_NUMBER="$(echo "$line" | awk -F: '{ print $1 }')"
+    TEST_INVOCATION="$(echo "$line" | awk -F: '{ print $2 }' | sed 's/^\# TEST //' | sed 's/ WITH$//')"
+
+    TEST_NAME="$(echo "$TEST_INVOCATION" | awk -F\( '{ print $1 }')"
+    TEST_ARGS="$(echo "$TEST_INVOCATION" | awk -F\( '{ print $2 }' | sed 's/)$//')"
+
+    has_with_contents="$(echo "$line" | sed 's/.*WITH$/true/')"
+    end_line="$TEST_LINE_NUMBER"
+
+    if [ "$has_with_contents" = "true" ]; then
+      for test_end in $all_test_end; do
+        if [ "$test_end" -gt "$TEST_LINE_NUMBER" ]; then
+          end_line="$test_end"
+          break
+        fi
+      done
+
+      TEST_CONTENTS_PATH="$TEST_TMP_FILES.$TEST_LINE_NUMBER.$end_line.partial"
+
+      awk -v start="$TEST_LINE_NUMBER" -v end="$end_line" 'NR > start && NR < end' "$TEST_FILE" \
+        | sed 's/^\# /#/' | sed 's/^\#//' > "$TEST_CONTENTS_PATH"
+    else
+      TEST_CONTENTS_PATH=""
+    fi
+
+    if type "$TEST_NAME" | grep -q 'function'; then
+        "$TEST_NAME"
+        print_passed
+    else
+        print_failed "test not implemented"
+    fi
+  done || exit 1;
+
+  cleanup
+  echo
+}
+
+get_test_args() {
+  arg_name="$1"
+  echo "$TEST_ARGS" | sed "s/,/\n/g" | grep "$arg_name=" | sed "s/^$arg_name=//"
+}
+
+assert_contents_path() {
+  if [ -z "$TEST_CONTENTS_PATH" ]; then
+    print_failed "missing WITH block for test contents"
+    exit 5
+  fi
+}
+
+# Test functions
+test_compile() {
+  expected_exit_code="$(get_test_args "exit_code")"
+  actual_output_file="$TEST_TMP_FILES.$TEST_LINE_NUMBER.compiler_output"
+
+  $OLANG_PATH "$TEST_FILE" -o "$TEST_TMP_BIN" > "$actual_output_file" 2>&1
+  exit_code="$?"
+
+  if [ -n "$expected_exit_code" ]; then
+    if [ "$expected_exit_code" -ne "$exit_code" ]; then
+      print_failed "expected compiler exit code: $expected_exit_code actual: $exit_code"
+      exit 1
+    fi
+  fi
+
+  if [ -n "$TEST_CONTENTS_PATH" ]; then
+    diff_output "$actual_output_file" "$TEST_CONTENTS_PATH"
+  fi
+}
+
+test_ast() {
+  assert_contents_path
+
+  actual_output_file="$TEST_TMP_FILES.$TEST_LINE_NUMBER.ast_output"
+
+  $OLANG_PATH "$TEST_FILE" --dump-ast > "$actual_output_file" 2>&1
+
+  diff_output "$actual_output_file" "$TEST_CONTENTS_PATH"
+}
+
+test_contains_tokens() {
+  assert_contents_path
+
+  actual_output_file="$TEST_TMP_FILES.$TEST_LINE_NUMBER.tokens_output"
+
+  $OLANG_PATH "$TEST_FILE" --dump-tokens > "$actual_output_file" 2>&1
+
+  expect_output_contains "$actual_output_file" "$TEST_CONTENTS_PATH"
+}
+
+test_run_binary() {
+  expected_exit_code="$(get_test_args "exit_code")"
+  actual_output_file="$TEST_TMP_FILES.$TEST_LINE_NUMBER.run_output"
+
+  "$TEST_TMP_BIN" > "$actual_output_file" 2>&1
+  exit_code="$?"
+
+  if [ -n "$expected_exit_code" ]; then
+    if [ "$expected_exit_code" -ne "$exit_code" ]; then
+      print_failed "expected compiler exit code: $expected_exit_code actual: $exit_code"
+      exit 1
+    fi
+  fi
+
+  if [ -n "$TEST_CONTENTS_PATH" ]; then
+    diff_output "$actual_output_file" "$TEST_CONTENTS_PATH"
+  fi
+}
+
+main
diff --git a/tests/integration/tests/0001_main_exit.ol b/tests/integration/tests/0001_main_exit.ol
new file mode 100644
index 0000000..f446eb9
--- /dev/null
+++ b/tests/integration/tests/0001_main_exit.ol
@@ -0,0 +1,46 @@
+# Copyright (C) 2024 olang mantainers
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# A minimal olang program
+fn main(): u32 {
+  return 0
+}
+
+# TEST test_compile(exit_code=0)
+#
+# TEST test_run_binary(exit_code=0)
+#
+# TEST test_ast WITH
+# Translation_Unit
+# `-Function_Definition <name:main> <return:0>
+#   `-Block
+#     `-Return_Statement
+#       `-Literal <kind:u32> <value:0>
+# END
+#
+# TEST test_contains_tokens WITH
+# ./tests/0001_main_exit.ol:17:1: <fn>
+# ./tests/0001_main_exit.ol:17:4: <identifier>
+# ./tests/0001_main_exit.ol:17:8: <(>
+# ./tests/0001_main_exit.ol:17:9: <)>
+# ./tests/0001_main_exit.ol:17:10: <:>
+# ./tests/0001_main_exit.ol:17:12: <identifier>
+# ./tests/0001_main_exit.ol:17:16: <{>
+# ./tests/0001_main_exit.ol:17:17: <line_feed>
+# ./tests/0001_main_exit.ol:18:3: <return>
+# ./tests/0001_main_exit.ol:18:10: <number>
+# ./tests/0001_main_exit.ol:18:11: <line_feed>
+# ./tests/0001_main_exit.ol:19:1: <}>
+# END
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang 4/4] tests: print integration tests TODOs
@ 2024-05-12 14:30 Carlos Maniero
  2024-05-12 14:31 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-05-12 14:30 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

Sometimes people use to ask how to contribute with olang. This is a
great way to help them, we can just create TODOS on the integration
tests as we already have some parts of the language specified.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 tests/integration/test.sh | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/tests/integration/test.sh b/tests/integration/test.sh
index 9fdabef..c8535a7 100755
--- a/tests/integration/test.sh
+++ b/tests/integration/test.sh
@@ -56,6 +56,14 @@ print_test_description() {
   printf "\n\n"
 }
 
+print_todo() {
+  TODOS=$(extract_comment TODO)
+
+  if [ -n "$TODOS" ]; then
+    colored "TODO: $(extract_comment TODO)\n\n" $COLOR_YELLOW
+  fi
+}
+
 print_passed() {
   context="$1"
   printf "%s: " "$context"
@@ -174,6 +182,7 @@ test_program() {
 
 main() {
   print_test_description
+  print_todo
 
   test_compiler
   test_program
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v1 2/2] codegen: x86_64: implement binary operations
@ 2024-04-27 12:14 Johnny Richard
  2024-04-27 11:21 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-04-27 12:14 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

In order to simplify most of the binary operation expressions we are
using stack to store previous execution.  It makes the implementation
easy but inefficient.

We can optimize this codegen in a near future as soon as re get IR in
place.

This change also include a small fix on binary operation precedence for
TOKEN_CMP_EQ.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 examples/sum.ol            |   3 +
 src/codegen_linux_x86_64.c | 232 ++++++++++++++++++++++++++++++++++++-
 src/parser.c               |   2 +-
 3 files changed, 232 insertions(+), 5 deletions(-)
 create mode 100644 examples/sum.ol

diff --git a/examples/sum.ol b/examples/sum.ol
new file mode 100644
index 0000000..b724088
--- /dev/null
+++ b/examples/sum.ol
@@ -0,0 +1,3 @@
+fn main(): u32 {
+  return 1 + 4 + 60 + 4
+}
diff --git a/src/codegen_linux_x86_64.c b/src/codegen_linux_x86_64.c
index 72db1c6..0accef4 100644
--- a/src/codegen_linux_x86_64.c
+++ b/src/codegen_linux_x86_64.c
@@ -23,6 +23,8 @@
 
 #define SYS_exit (60)
 
+size_t label_index;
+
 static void
 codegen_linux_x86_64_emit_start_entrypoint(FILE *out);
 
@@ -32,6 +34,7 @@ codegen_linux_x86_64_emit_function(FILE *out, ast_fn_definition_t *fn);
 void
 codegen_linux_x86_64_emit_program(FILE *out, ast_node_t *node)
 {
+    label_index = 0;
     codegen_linux_x86_64_emit_start_entrypoint(out);
 
     assert(node->kind == AST_NODE_PROGRAM);
@@ -56,6 +59,12 @@ codegen_linux_x86_64_emit_start_entrypoint(FILE *out)
     fprintf(out, "    syscall\n");
 }
 
+static size_t
+codegen_linux_x86_64_get_next_label(void)
+{
+    return ++label_index;
+}
+
 static void
 codegen_linux_x86_64_emit_expression(FILE *out, ast_node_t *expr_node)
 {
@@ -63,14 +72,229 @@ codegen_linux_x86_64_emit_expression(FILE *out, ast_node_t *expr_node)
         case AST_NODE_LITERAL: {
             ast_literal_t literal_u32 = expr_node->data.as_literal;
             assert(literal_u32.kind == AST_LITERAL_U32);
-            uint32_t exit_code = literal_u32.value.as_u32;
+            uint32_t n = literal_u32.value.as_u32;
 
-            fprintf(out, "    mov $%d, %%eax\n", exit_code);
+            fprintf(out, "    mov $%d, %%rax\n", n);
             return;
         }
-        case AST_NODE_BINARY_OP:
+        case AST_NODE_BINARY_OP: {
+            ast_binary_op_t bin_op = expr_node->data.as_bin_op;
+            switch (bin_op.kind) {
+                case AST_BINOP_ADDITION: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    add %%rcx, %%rax\n");
+
+                    return;
+                }
+                case AST_BINOP_MULTIPLICATION: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    mul %%rcx\n");
+
+                    return;
+                }
+                case AST_BINOP_DIVISION: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    div %%rcx\n");
+
+                    return;
+                }
+                case AST_BINOP_REMINDER: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+                    fprintf(out, "    xor %%edx, %%edx\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    div %%rcx\n");
+                    fprintf(out, "    mov %%edx, %%eax\n");
+
+                    return;
+                }
+                case AST_BINOP_SUBTRACTION: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    sub %%rcx, %%rax\n");
+
+                    return;
+                }
+                case AST_BINOP_CMP_EQ: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    cmp %%rcx, %%rax\n");
+                    fprintf(out, "    sete %%al\n");
+                    fprintf(out, "    movzb %%al, %%rax\n");
+
+                    return;
+                }
+                case AST_BINOP_CMP_LT: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    cmp %%rcx, %%rax\n");
+                    fprintf(out, "    setl %%al\n");
+                    fprintf(out, "    movzb %%al, %%rax\n");
+
+                    return;
+                }
+                case AST_BINOP_CMP_GT: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    cmp %%rcx, %%rax\n");
+                    fprintf(out, "    setg %%al\n");
+                    fprintf(out, "    movzb %%al, %%rax\n");
+
+                    return;
+                }
+                case AST_BINOP_CMP_NEQ: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    cmp %%rcx, %%rax\n");
+                    fprintf(out, "    setne %%al\n");
+                    fprintf(out, "    movzb %%al, %%rax\n");
+
+                    return;
+                }
+                case AST_BINOP_CMP_LEQ: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    cmp %%rcx, %%rax\n");
+                    fprintf(out, "    setle %%al\n");
+                    fprintf(out, "    movzb %%al, %%rax\n");
+
+                    return;
+                }
+                case AST_BINOP_CMP_GEQ: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    cmp %%rcx, %%rax\n");
+                    fprintf(out, "    setge %%al\n");
+                    fprintf(out, "    movzb %%al, %%rax\n");
+
+                    return;
+                }
+                case AST_BINOP_BITWISE_LSHIFT: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    shl %%cl, %%rax\n");
+
+                    return;
+                }
+                case AST_BINOP_BITWISE_RSHIFT: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    shr %%cl, %%rax\n");
+
+                    return;
+                }
+                case AST_BINOP_BITWISE_XOR: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    xor %%ecx, %%eax\n");
+
+                    return;
+                }
+                case AST_BINOP_BITWISE_AND: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    and %%ecx, %%eax\n");
+
+                    return;
+                }
+                case AST_BINOP_BITWISE_OR: {
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    push %%rax\n");
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    pop %%rcx\n");
+                    fprintf(out, "    or %%ecx, %%eax\n");
+
+                    return;
+                }
+                case AST_BINOP_LOGICAL_AND: {
+                    size_t label_exit = codegen_linux_x86_64_get_next_label();
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    cmp $0, %%rax\n");
+                    fprintf(out, "    je .L%ld\n", label_exit);
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    cmp $0, %%rax\n");
+                    fprintf(out, "    je .L%ld\n", label_exit);
+                    fprintf(out, "    mov $1, %%rax\n");
+                    fprintf(out, ".L%ld:\n", label_exit);
+
+                    return;
+                }
+                case AST_BINOP_LOGICAL_OR: {
+                    size_t label_t = codegen_linux_x86_64_get_next_label();
+                    size_t label_f = codegen_linux_x86_64_get_next_label();
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.lhs);
+                    fprintf(out, "    cmp $0, %%rax\n");
+                    fprintf(out, "    jne .L%ld\n", label_t);
+
+                    codegen_linux_x86_64_emit_expression(out, bin_op.rhs);
+                    fprintf(out, "    cmp $0, %%rax\n");
+                    fprintf(out, "    je .L%ld\n", label_f);
+
+                    fprintf(out, ".L%ld:\n", label_t);
+                    fprintf(out, "    mov $1, %%rax\n");
+                    fprintf(out, ".L%ld:\n", label_f);
+
+                    return;
+                }
+                default: {
+                    assert(0 && "unsupported binary operation");
+                    return;
+                }
+            }
+        }
         default:
-            assert(0 && "NOT IMPLEMENTED");
+            assert(0 && "unsupported expression");
     }
 }
 
diff --git a/src/parser.c b/src/parser.c
index 5ba5a3f..5dd4ef1 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -152,7 +152,7 @@ get_binary_op_precedence(token_kind_t kind)
         case TOKEN_CMP_LEQ:
         case TOKEN_CMP_GEQ:
             return BINOP_CMP_RELATIONAL_PREC;
-        case TOKEN_EQ:
+        case TOKEN_CMP_EQ:
         case TOKEN_CMP_NEQ:
             return BINOP_CMP_EQUALITY_PREC;
         case TOKEN_AND:
-- 
2.44.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v1] parser: fix parse expression with binop chain
@ 2024-04-18 23:08 Johnny Richard
  2024-04-18 22:11 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-04-18 23:08 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

The parsing of expression was dropping the lhs with higher precedence
due to a bug where we never changed the first lhs.

Consider the following code:

    1  fn main(): u32 {
    2    return 1 * 2 + 3
    3  }

With the bug it is generating the following AST:

    Translation_Unit
    `-Function_Definition <name:main> <return:0>
      `-Block
        `-Return_Statement
          `-Binary_Operation (+)
            |-Literal <kind:u32> <value:1>
            `-Literal <kind:u32> <value:3>

After this fix being applied, the same code generates the following AST:

    Translation_Unit
    `-Function_Definition <name:main> <return:0>
      `-Block
        `-Return_Statement
          `-Binary_Operation (+)
            |-Binary_Operation (*)
            | |-Literal <kind:u32> <value:1>
            | `-Literal <kind:u32> <value:2>
            `-Literal <kind:u32> <value:3>

As soon as we get a better functional test for the compiler we can cover
these corner cases easily.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/parser.c | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/src/parser.c b/src/parser.c
index b800870..db79072 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -171,8 +171,6 @@ get_binary_op_precedence(token_kind_t kind)
 static ast_node_t *
 parser_parse_expr_1(parser_t *parser, ast_node_t *lhs, size_t prev_precedence)
 {
-    ast_node_t *expr = NULL;
-
     token_t lookahead_token;
     lexer_peek_next(parser->lexer, &lookahead_token);
 
@@ -194,17 +192,13 @@ parser_parse_expr_1(parser_t *parser, ast_node_t *lhs, size_t prev_precedence)
             lexer_peek_next(parser->lexer, &lookahead_token);
         }
 
-        expr = ast_new_node_bin_op(parser->arena, token_kind_to_binary_op_kind(token_op.kind), lhs, rhs);
-        if (expr == NULL) {
+        lhs = ast_new_node_bin_op(parser->arena, token_kind_to_binary_op_kind(token_op.kind), lhs, rhs);
+        if (lhs == NULL) {
             return NULL;
         }
     }
 
-    if (expr == NULL) {
-        return lhs;
-    }
-
-    return expr;
+    return lhs;
 }
 
 static ast_node_t *

base-commit: dc6b44ddd2673a9a377430684ce37a80fbbaace3
-- 
2.44.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v1] parser: add missing <= and >= binary operators
@ 2024-04-18 22:18 Johnny Richard
  2024-04-18 21:22 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-04-18 22:18 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/parser.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/parser.c b/src/parser.c
index b800870..db862d8 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -126,8 +126,8 @@ typedef enum
     BINOP_BITWISE_OR_PREC,
     BINOP_BITWISE_XOR_PREC,
     BINOP_BITWISE_AND_PREC,
-    BINOP_CMP_EQ_AND_NEQ_PREC,
-    BINOP_CMP_LT_AND_GT_PREC,
+    BINOP_CMP_EQUALITY_PREC,
+    BINOP_CMP_RELATIONAL_PREC,
     BINOP_BITWISE_SHIFT_PREC,
     BINOP_ADDITIVE_PREC,
     BINOP_MULTIPLICATIVE_PREC,
@@ -149,10 +149,12 @@ get_binary_op_precedence(token_kind_t kind)
             return BINOP_BITWISE_SHIFT_PREC;
         case TOKEN_LT:
         case TOKEN_GT:
-            return BINOP_CMP_LT_AND_GT_PREC;
+        case TOKEN_CMP_LEQ:
+        case TOKEN_CMP_GEQ:
+            return BINOP_CMP_RELATIONAL_PREC;
         case TOKEN_EQ:
         case TOKEN_CMP_NEQ:
-            return BINOP_CMP_EQ_AND_NEQ_PREC;
+            return BINOP_CMP_EQUALITY_PREC;
         case TOKEN_AND:
             return BINOP_BITWISE_AND_PREC;
         case TOKEN_CIRCUMFLEX:

base-commit: dc6b44ddd2673a9a377430684ce37a80fbbaace3
-- 
2.44.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v1] docs: spec: add %, <= and >= binary operators
@ 2024-04-18 21:58 Johnny Richard
  2024-04-18 21:02 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-04-18 21:58 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard, Ricardo Kagawa

Reported-by: Ricardo Kagawa <ricardo_kagawa@disroot.org>
Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 docs/pages/language-specification.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/docs/pages/language-specification.md b/docs/pages/language-specification.md
index b5a55c1..747bb53 100644
--- a/docs/pages/language-specification.md
+++ b/docs/pages/language-specification.md
@@ -66,12 +66,12 @@ language.
 <logical-and-expression> ::= <bitwise-or-expression> (<ows> '&&' <ows> <bitwise-or-expression>)*
 <bitwise-or-expression> ::= <bitwise-xor-expression> (<ows> '|' <ows> <bitwise-xor-expression>)*
 <bitwise-xor-expression> ::= <bitwise-and-expression> (<ows> '^' <ows> <bitwise-and-expression>)*
-<bitwise-and-expression> ::= <cmp-eq-and-neq-expression> (<ows> '&' <ows> <cmp-eq-and-neq-expression>)*
-<cmp-eq-and-neq-expression> ::= <cmp-lt-and-gt-expression> (<ows> ('==' | '!=') <ows> <cmp-lt-and-gt-expression>)*
-<cmp-lt-and-gt-expression> ::= <bitwise-shift-expression> (<ows> ('<' | '>') <ows> <bitwise-shift-expression>)*
+<bitwise-and-expression> ::= <cmp-equality-expression> (<ows> '&' <ows> <cmp-equality-expression>)*
+<cmp-equality-expression> ::= <cmp-relational-expression> (<ows> ('==' | '!=') <ows> <cmp-relational-expression>)*
+<cmp-relational-expression> ::= <bitwise-shift-expression> (<ows> ('<' | '>' | '<=' | '>=') <ows> <bitwise-shift-expression>)*
 <bitwise-shift-expression> ::= <additive-expression> (<ows> ('<<' | '>>') <ows> <additive-expression>)*
 <additive-expression> ::= <multiplicative-expression> (<ows> ('+' | '-') <ows> <multiplicative-expression>)*
-<multiplicative-expression> ::= <primary-expression> (<ows> ('*' | '/') <ows> <primary-expression>)*
+<multiplicative-expression> ::= <primary-expression> (<ows> ('*' | '/' | '%') <ows> <primary-expression>)*
 <primary-expression> ::= <integer-literal>
                        | <variable-name>
                        | '(' <ows>  <expression> <ows> ')'

base-commit: dc6b44ddd2673a9a377430684ce37a80fbbaace3
-- 
2.44.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v1] Revert "docs: spec: postpone assignment operators"
@ 2024-04-16 23:51 Johnny Richard
  2024-04-16 22:56 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-04-16 23:51 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard, Ricardo Kagawa

This reverts commit 00469200f51f96921d31310af587a1f5b808d87a.
---
 docs/pages/language-specification.md | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/docs/pages/language-specification.md b/docs/pages/language-specification.md
index a9931c1..b5a55c1 100644
--- a/docs/pages/language-specification.md
+++ b/docs/pages/language-specification.md
@@ -48,6 +48,16 @@ language.
 <common-statement>    ::= <variable-definition> | <constant-definition> | <assignment>
 <assignment>          ::= <variable-name> <ows> <assignment-operator> <ows> <expression>
 <assignment-operator> ::= '='
+                        | '*='
+                        | '/='
+                        | '%='
+                        | '+='
+                        | '-='
+                        | '<<='
+                        | '>>='
+                        | '&='
+                        | '^='
+                        | '|='
 
 (* Expressions *)
 <expression> ::= <binary-expression>
-- 
2.44.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v2] docs: spec: add binary expressions
@ 2024-04-16 23:35 Johnny Richard
  2024-04-16 22:40 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-04-16 23:35 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard, Ricardo Kagawa

The parser already implements binary operations, but currently we are
missing it on the language specification.

This change adds the already implemented binary operations grammar and
also enables expression with parenthesis.

It's important to mention that the precedence was highly inspired on C.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
v1 -> v2: rebase on current main branch.

 docs/pages/language-specification.md | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/docs/pages/language-specification.md b/docs/pages/language-specification.md
index 3cf050e..a9931c1 100644
--- a/docs/pages/language-specification.md
+++ b/docs/pages/language-specification.md
@@ -50,7 +50,21 @@ language.
 <assignment-operator> ::= '='
 
 (* Expressions *)
-<expression>          ::= <integer-literal> | <variable-name>
+<expression> ::= <binary-expression>
+<binary-expression> ::= <logical-or-expression>
+<logical-or-expression> ::= <logical-and-expression> (<ows> '||' <ows> <logical-and-expression>)*
+<logical-and-expression> ::= <bitwise-or-expression> (<ows> '&&' <ows> <bitwise-or-expression>)*
+<bitwise-or-expression> ::= <bitwise-xor-expression> (<ows> '|' <ows> <bitwise-xor-expression>)*
+<bitwise-xor-expression> ::= <bitwise-and-expression> (<ows> '^' <ows> <bitwise-and-expression>)*
+<bitwise-and-expression> ::= <cmp-eq-and-neq-expression> (<ows> '&' <ows> <cmp-eq-and-neq-expression>)*
+<cmp-eq-and-neq-expression> ::= <cmp-lt-and-gt-expression> (<ows> ('==' | '!=') <ows> <cmp-lt-and-gt-expression>)*
+<cmp-lt-and-gt-expression> ::= <bitwise-shift-expression> (<ows> ('<' | '>') <ows> <bitwise-shift-expression>)*
+<bitwise-shift-expression> ::= <additive-expression> (<ows> ('<<' | '>>') <ows> <additive-expression>)*
+<additive-expression> ::= <multiplicative-expression> (<ows> ('+' | '-') <ows> <multiplicative-expression>)*
+<multiplicative-expression> ::= <primary-expression> (<ows> ('*' | '/') <ows> <primary-expression>)*
+<primary-expression> ::= <integer-literal>
+                       | <variable-name>
+                       | '(' <ows>  <expression> <ows> ')'
 
 (* Identifiers *)
 <type>                ::= 'u32'

base-commit: 00469200f51f96921d31310af587a1f5b808d87a -- 
2.44.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v1] spec: ebnf: add binary expressions
@ 2024-04-15 18:20 Johnny Richard
  2024-04-15 17:43 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-04-15 18:20 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard, Ricardo Kagawa

The parser already implements binary operations, but currently we are
missing it on the language specification.

This change adds the already implemented binary operations grammar and
also enables expression with parenthesis.  

It's important to mention that the precedence was highly inspired on C.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---

Ricardo, this is the missing spec I mentioned on your reply on variable
PATCH[1].  Let me know if this change make sense.

NOTE: This PATCH has been implemented on top of the commit mentioned
(base-commit) in the bottom of this PATCH.

[1]: Message-ID: <sm7jzr3mklfxscefg25cdhnrtjqldawquvgq6cdczi5kfyt4my@upgwv63nrzw7>

 docs/pages/language-specification.md | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/docs/pages/language-specification.md b/docs/pages/language-specification.md
index 4d0eb36..ff0a215 100644
--- a/docs/pages/language-specification.md
+++ b/docs/pages/language-specification.md
@@ -46,7 +46,22 @@ language.
 <return-statement>    ::= 'return' <ws> <expression>
 
 (* Expressions *)
-<expression>          ::= <integer> | <identifier>
+<expression>                ::= <binary-expression>
+<binary-expression>         ::= <logical-or-expression>
+<logical-or-expression>     ::= <logical-and-expression> (<ows> '||' <ows> <logical-and-expression>)*
+<logical-and-expression>    ::= <bitwise-or-expression> (<ows> '&&' <ows> <bitwise-or-expression>)*
+<bitwise-or-expression>     ::= <bitwise-xor-expression> (<ows> '|' <ows> <bitwise-xor-expression>)*
+<bitwise-xor-expression>    ::= <bitwise-and-expression> (<ows> '^' <ows> <bitwise-and-expression>)*
+<bitwise-and-expression>    ::= <cmp-eq-and-neq-expression> (<ows> '&' <ows> <cmp-eq-and-neq-expression>)*
+<cmp-eq-and-neq-expression> ::= <cmp-lt-and-gt-expression> (<ows> ('==' | '!=') <ows> <cmp-lt-and-gt-expression>)*
+<cmp-lt-and-gt-expression>  ::= <bitwise-shift-expression> (<ows> ('<' | '>') <ows> <bitwise-shift-expression>)*
+<bitwise-shift-expression>  ::= <additive-expression> (<ows> ('<<' | '>>') <ows> <additive-expression>)*
+<additive-expression>       ::= <multiplicative-expression> (<ows> ('+' | '-') <ows> <multiplicative-expression>)*
+<multiplicative-expression> ::= <primary-expression> (<ows> ('*' | '/') <ows> <primary-expression>)*
+<primary-expression>        ::= <integer>
+                              | <identifier>
+                              | '(' <ows>  <expression> <ows> ')'
+
 <assign-expression>   ::= <variable-name> <ows> <assign-operator> <ows> <expression>
 <assign-operator>     ::= '='
                         | '*='

base-commit: 7cd840ea689751643d2dd022b81d12d9581800cb
-- 
2.44.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v2 2/2] docs: spec: add variables and constants specification
@ 2024-04-08  4:38 Carlos Maniero
  2024-04-08  4:39 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-04-08  4:38 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

This commit introduces the specification for variables and constants. A
valid program under this specification is as follows:

  var x: u32 = 1
  const y: u32 = 2

  fn main(): u32 {
    var x: u32 = 1; const y: u32 = 2
    return x
  }

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 docs/pages/language-specification.md | 33 ++++++++++++++++++++++------
 1 file changed, 26 insertions(+), 7 deletions(-)

diff --git a/docs/pages/language-specification.md b/docs/pages/language-specification.md
index 544741a..4d0eb36 100644
--- a/docs/pages/language-specification.md
+++ b/docs/pages/language-specification.md
@@ -22,24 +22,43 @@ language.
 
 ```
 (* Entry Point *)
-<translation-unit>    ::= <ows> <function-definition> <ows> <end-of-file>
+<translation-unit>     ::= (<ows> <external-declaration> <ows> (<end-of-statement> | <end-of-file>))*
+
+<external-declaration> ::= <function-definition> | <variable-definition>
+
+(* Variables *)
+<variable-definition>  ::= <variable-qualifier> <ws> <variable-name> <ows> ':' <ows> <type> (<ows> <assign-operator> <ows> <expression>)?
+<variable-qualifier>   ::= 'var'
+                         | 'const'
+<variable-name>        ::= <identifier>
+
 (* Functions *)
-<function-definition> ::= 'fn' <ws> <function-name> <ows>
-<function-parameters> <ows> ':' <ows> <return-type> <ows> <function-body>
+<function-definition> ::= 'fn' <ws> <function-name> <ows> <function-parameters> <ows> ':' <ows> <return-type> <ows> <function-body>
 <function-name>       ::= <identifier>
 <function-parameters> ::= '(' <ows> ')'
 <return-type>         ::= <type>
 <function-body>       ::= <block>
 
 (* Statements *)
-<block>               ::= '{' <ows> <statement> <ows> (<end-of-statement>
-<ows> <statement> <ows>)* <end-of-statement>? <ows> '}'
+<block>               ::= '{' <ows> <statement> <ows> (<end-of-statement> <ows> <statement> <ows>)* <end-of-statement>? <ows> '}'
 <end-of-statement>    ::= ';' | <line-break>
-<statement>           ::= <return-statement>
+<statement>           ::= <return-statement> | <variable-definition> | <assign-expression>
 <return-statement>    ::= 'return' <ws> <expression>
 
 (* Expressions *)
-<expression>          ::= <integer>
+<expression>          ::= <integer> | <identifier>
+<assign-expression>   ::= <variable-name> <ows> <assign-operator> <ows> <expression>
+<assign-operator>     ::= '='
+                        | '*='
+                        | '/='
+                        | '%='
+                        | '+='
+                        | '-='
+                        | '<<='
+                        | '>>='
+                        | '&='
+                        | '^='
+                        | '|='
 
 (* Identifiers *)
 <type>                ::= 'u32'
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang] linter: turn off clang-format to keep retro compatibility with v16
@ 2024-03-29  1:59 Johnny Richard
  2024-03-29  0:59 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-03-29  1:59 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Clang-format version 17 breaks compatibility with version 16, we want to
keep retro compatibility with 16 since we have other contributors which
wont be able to easily migrate.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/pretty_print_ast.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/pretty_print_ast.c b/src/pretty_print_ast.c
index 129f090..548e38e 100644
--- a/src/pretty_print_ast.c
+++ b/src/pretty_print_ast.c
@@ -26,7 +26,10 @@
 
 #define ANSI_COLOR_MAGENTA "\x1b[35m"
 #define ANSI_COLOR_RESET "\x1b[0m"
+
+// clang-format off
 #define PP_IS_BIT_SET(data, index) ((data) & 1 << index)
+// clang-format on
 
 typedef struct pretty_print_node
 {
-- 
2.44.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang] site: change look and feel and rewrite home introduction section
@ 2024-03-29  0:33 Johnny Richard
  2024-03-28 23:33 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-03-29  0:33 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 docs/index.md                            | 34 +++++++---------------
 docs/pages/{hacking.md => contribute.md} |  7 ++---
 docs/pages/getting-started.md            |  2 +-
 docs/template.html                       | 37 +++++++++++++++++-------
 src/pretty_print_ast.c                   |  2 +-
 5 files changed, 42 insertions(+), 40 deletions(-)
 rename docs/pages/{hacking.md => contribute.md} (97%)

diff --git a/docs/index.md b/docs/index.md
index 1a28069..82c4ef3 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,27 +1,15 @@
-% Welcome to olang documentation
+% Introduction
 
-The O Programming Language.
+**[WARNING] THIS SOFTWARE IS UNFINISHED AND NOT PRODUCTION READY**
 
-## olang manifest
+The O language is a system programming language crafted to be remarkably simple
+and flexible. It stands not as a replacement for C, but rather as a
+complementary counterpart, as both can coexist within the same source code. O
+language boasts minimal abstraction, ensuring seamless integration with C and
+almost predictable machine code.
 
-We, as developers, value the modern programming languages that allow us to
-produce reliable code in a short time span. However, we, as programmers, also
-lament that these same languages have diminished the joy of programming by
-imposing unnecessary constraints and complexities on our creative expression.
+olang is a deterministic system language that follows tree principles:
 
-That is why we are creating a new programming language that may not be suitable
-for most commercial applications, but that can be enjoyed in the places where
-it is meant to be used: on system applications.
-
-olang is a deterministic system language that follows two principles:
-
-- **olang fights complexity** by providing a simple syntax with a low level of abstraction;
-- **olang doesn't babysit programmers** and therefore the compiler only checks semantics.
-
-olang maintainers
-
-### Subscribe olang's development mailing list.
-
-If you want to subscribe to the mailing list, you can achieve it by sending an
-email to
-[~johnnyrichard/olang-devel+subscribe@lists.sr.ht](mailto:~johnnyrichard/olang-devel+subscribe@lists.sr.ht?subject=Subscribe&body=Subscribe)
+- **fights complexity** by providing a simple syntax with a low level of abstraction.
+- **doesn't babysit programmers**, it gives you the freedom of shooting your own foot.
+- **aims easy maintainability** by providing a syntax with a low refactoring overhead.
diff --git a/docs/pages/hacking.md b/docs/pages/contribute.md
similarity index 97%
rename from docs/pages/hacking.md
rename to docs/pages/contribute.md
index fe8f705..884c4b4 100644
--- a/docs/pages/hacking.md
+++ b/docs/pages/contribute.md
@@ -1,11 +1,10 @@
-% Hacking
+% Contribute
 
 We're thrilled to have you here! Your interest in making olang the most
 exceptional and straightforward language ever is greatly appreciated.
 
-In this document, we'll outline how you can begin contributing to olang.
-
-First and foremost, clone the project if you haven't done so already.
+In this document, we'll outline how you can begin contributing to olang.  First
+and foremost, clone the project if you haven't done so already.
 
 ``` {.sh}
 git clone https://git.sr.ht/~johnnyrichard/olang
diff --git a/docs/pages/getting-started.md b/docs/pages/getting-started.md
index af258df..eb4822a 100644
--- a/docs/pages/getting-started.md
+++ b/docs/pages/getting-started.md
@@ -1,3 +1,3 @@
-% Getting stated (WIP)
+% Getting stated
 
 WIP
diff --git a/docs/template.html b/docs/template.html
index 52a22d1..774ab49 100644
--- a/docs/template.html
+++ b/docs/template.html
@@ -21,8 +21,7 @@
     body {
       background: var(--background-color);
       color: var(--text-color);
-      font-family: monospace;
-      font-size: 1.1rem;
+      font-size: 1.4rem;
       margin: 0;
       padding: 0 40px;
     }
@@ -33,8 +32,18 @@
       max-width: 1024px;
       margin: 10px auto;
     }
-    header, footer {
-      margin: 70px auto;
+    header > h1 {
+      font-weight: normal;
+    }
+    nav, header > h1 {
+      text-align: center;
+    }
+    header {
+      margin-top: 70px;
+      margin-bottom: 20px;
+    }
+    footer {
+      margin: 20px auto;
     }
     article {
       line-height: 1.75rem;
@@ -47,19 +56,25 @@
       padding: 10px;
       max-width: 100%;
       overflow: auto;
+      font-size: large;
+    }
+
+    .logo-name {
+      font-size: 3.2rem;
+      font-weight: bold;
     }
   </style>
 </head>
 <body>
   <header>
-    <h1>olang | The O Programming Language</h1>
+    <h1><span class="logo-name">olang</span> | O Programming Language</h1>
     <nav>
-      <a href="/">Index</a> |
-      <a href="/pages/getting-started.html">Getting started (WIP)</a> |
-      <a href="/pages/language-specification.html">Specification</a> |
-      <a href="/pages/hacking.html">Hacking</a> |
-      <a href="https://sr.ht/~johnnyrichard/olang/sources" target="_blank">Sources ↗</a> |
-      <a href="https://sr.ht/~johnnyrichard/olang/lists" target="_blank">Mailing list ↗</a>
+      [ <a href="/">Home</a> ]
+      [ <a href="/pages/getting-started.html">Getting started</a> ]
+      [ <a href="/pages/language-specification.html">Specification</a> ]
+      [ <a href="/pages/contribute.html">Contribute</a> ]
+      [ <a href="https://sr.ht/~johnnyrichard/olang/sources" target="_blank">Sources ↗</a> ]
+      [ <a href="https://sr.ht/~johnnyrichard/olang/lists" target="_blank">Mailing list ↗</a> ]
     </nav>
   </header>
   <article>
diff --git a/src/pretty_print_ast.c b/src/pretty_print_ast.c
index e950796..129f090 100644
--- a/src/pretty_print_ast.c
+++ b/src/pretty_print_ast.c
@@ -26,7 +26,7 @@
 
 #define ANSI_COLOR_MAGENTA "\x1b[35m"
 #define ANSI_COLOR_RESET "\x1b[0m"
-#define PP_IS_BIT_SET(data, index) ((data)&1 << index)
+#define PP_IS_BIT_SET(data, index) ((data) & 1 << index)
 
 typedef struct pretty_print_node
 {
-- 
2.44.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v3] docs: create o programming language spec
@ 2024-03-24 16:12 Johnny Richard
  2024-03-24 15:16 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-03-24 16:12 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard, Ricardo Kagawa

This document specifies the semantics and behavior of the O Programming
Language for compiler designers be informed on how the language is designed.

This document will help newcomers to understand how the language looks
like and also as a DRAFT guide to drive design discussions.

The grammar was made by using a EBNF evaluator tool[1].

[1]: The live example https://mdkrajnak.github.io/ebnftest/ and a locked
     version https://github.com/Engelberg/instaparse/tree/v1.4.12 as
     reference.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
Co-authored-by: Ricardo Kagawa <ricardo.kagawa@gmail.com>
---
V3: 
  - fix markdown reference link
  - add Pandoc metadata to fix page header
  - add spec link to layout header
  - fix page max-width to avoid EBNF of wrapping lines on big screens

 docs/pages/language-specification.md | 64 ++++++++++++++++++++++++++++
 docs/template.html                   |  3 +-
 2 files changed, 66 insertions(+), 1 deletion(-)
 create mode 100644 docs/pages/language-specification.md

diff --git a/docs/pages/language-specification.md b/docs/pages/language-specification.md
new file mode 100644
index 0000000..5769d95
--- /dev/null
+++ b/docs/pages/language-specification.md
@@ -0,0 +1,64 @@
+% O programming language specification
+
+Abstract
+--------
+
+This document specifies the semantics and behavior of the O Programming
+Language for compiler designers be informed how the language is designed.
+
+This specification is a DRAFT and will be the discussions drive over olang-dev
+mailing list.
+
+Language Syntax
+---------------
+
+This is the O Programming Language EBNF grammar specification[^1]
+
+[^1]: EBNF variant https://github.com/Engelberg/instaparse/tree/v1.4.12 and live
+      test can be accessed here https://mdkrajnak.github.io/ebnftest/
+
+NOTE: This grammar spec is a DRAFT and it covers only a small portion of the
+language.
+
+```
+(* Entry Point *)
+<program>             ::= <ows> <function-definition> <ows> <end-of-file>
+
+(* Functions *)
+<function-definition> ::= 'fn' <ws> <function-name> <ows>
+<function-parameters> <ows> ':' <ows> <return-type> <ows> <function-body>
+<function-name>       ::= <identifier>
+<function-parameters> ::= '(' <ows> ')'
+<return-type>         ::= <type>
+<function-body>       ::= <block>
+
+(* Statements *)
+<block>               ::= '{' <ows> <statement> <ows> (<end-of-statement>
+<ows> <statement> <ows>)* <end-of-statement>? <ows> '}'
+<end-of-statement>    ::= ';' | <line-break>
+<statement>           ::= <return-statement>
+<return-statement>    ::= 'return' <ws> <expression>
+
+(* Expressions *)
+<expression>          ::= <integer>
+
+(* Identifiers *)
+<type>                ::= 'u32'
+<identifier>          ::= (<alpha> | '_') (<alpha> | <digit> | '_')*
+
+(* Literals *)
+<integer>             ::= <integer-base10> | <integer-base16>
+<integer-base10>      ::= #'[1-9]' (<digit> | '_')* | '0'
+<integer-base16>      ::= #'0[Xx]' <hex-digit> (<hex-digit> | '_')*
+
+(* Utilities *)
+<ws>                  ::= <white-space>+
+<ows>                 ::= <white-space>*
+<white-space>         ::= <linear-space> | <line-break>
+<line-break>          ::= #'[\n\v\f\r]' | '\r\n'
+<linear-space>        ::= #'[ \t]'
+<alpha>               ::= #'[a-zA-Z]'
+<digit>               ::= #'[0-9]'
+<hex-digit>           ::= <digit> | #'[a-fA-F]'
+<end-of-file>         ::= #'$'
+```
diff --git a/docs/template.html b/docs/template.html
index 4e066d1..52a22d1 100644
--- a/docs/template.html
+++ b/docs/template.html
@@ -30,7 +30,7 @@
       color: var(--link-color);
     }
     article, nav, header, footer {
-      max-width: 820px;
+      max-width: 1024px;
       margin: 10px auto;
     }
     header, footer {
@@ -56,6 +56,7 @@
     <nav>
       <a href="/">Index</a> |
       <a href="/pages/getting-started.html">Getting started (WIP)</a> |
+      <a href="/pages/language-specification.html">Specification</a> |
       <a href="/pages/hacking.html">Hacking</a> |
       <a href="https://sr.ht/~johnnyrichard/olang/sources" target="_blank">Sources ↗</a> |
       <a href="https://sr.ht/~johnnyrichard/olang/lists" target="_blank">Mailing list ↗</a>
-- 
2.44.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v2] docs: create o programming language spec
@ 2024-03-19 20:18 Johnny Richard
  2024-03-19 19:20 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-03-19 20:18 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard, Ricardo Kagawa

This document specifies the semantics and behavior of the O Programming
Language for compiler designers be informed on how the language is designed.

This document will help newcomers to understand how the language looks
like and also as a DRAFT guide to drive design discussions.

The grammar was made by using a EBNF evaluator tool[1].

[1]: The live example https://mdkrajnak.github.io/ebnftest/ and a locked
     version https://github.com/Engelberg/instaparse/tree/v1.4.12 as
     reference.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 docs/pages/language-specification.md | 65 ++++++++++++++++++++++++++++
 1 file changed, 65 insertions(+)
 create mode 100644 docs/pages/language-specification.md

diff --git a/docs/pages/language-specification.md b/docs/pages/language-specification.md
new file mode 100644
index 0000000..17e6eed
--- /dev/null
+++ b/docs/pages/language-specification.md
@@ -0,0 +1,65 @@
+O programming language specification
+====================================
+
+ABSTRACT
+--------
+
+This document specifies the semantics and behavior of the O Programming
+Language for compiler designers be informed how the language is designed.
+
+This specification is a DRAFT and will be the discussions drive over olang-dev
+mailing list.
+
+Language Syntax
+---------------
+
+This is the O Programming Language EBNF grammar specification[1]
+
+[1]: EBNF variant https://github.com/Engelberg/instaparse/tree/v1.4.12 and live
+     test can be accessed here https://mdkrajnak.github.io/ebnftest/ 
+
+NOTE: This grammar spec is a DRAFT and it covers only a small portion of the
+language.
+
+```
+(* Entry Point *)
+<program>             ::= <ows> <function-definition> <ows> <end-of-file>
+
+(* Functions *)
+<function-definition> ::= 'fn' <ws> <function-name> <ows>
+<function-parameters> <ows> ':' <ows> <return-type> <ows> <function-body>
+<function-name>       ::= <identifier>
+<function-parameters> ::= '(' <ows> ')'
+<return-type>         ::= <type>
+<function-body>       ::= <block>
+
+(* Statements *)
+<block>               ::= '{' <ows> <statement> <ows> (<end-of-statement>
+<ows> <statement> <ows>)* <end-of-statement>? <ows> '}'
+<end-of-statement>    ::= ';' | <line-break>
+<statement>           ::= <return-statement>
+<return-statement>    ::= 'return' <ws> <expression>
+
+(* Expressions *)
+<expression>          ::= <integer>
+
+(* Identifiers *)
+<type>                ::= 'u32'
+<identifier>          ::= (<alpha> | '_') (<alpha> | <digit> | '_')*
+
+(* Literals *)
+<integer>             ::= <integer-base10> | <integer-base16>
+<integer-base10>      ::= #'[1-9]' (<digit> | '_')* | '0'
+<integer-base16>      ::= #'0[Xx]' <hex-digit> (<hex-digit> | '_')*
+
+(* Utilities *)
+<ws>                  ::= <white-space>+
+<ows>                 ::= <white-space>*
+<white-space>         ::= <linear-space> | <line-break>
+<line-break>          ::= #'[\n\v\f\r]' | '\r\n'
+<linear-space>        ::= #'[ \t]'
+<alpha>               ::= #'[a-zA-Z]'
+<digit>               ::= #'[0-9]'
+<hex-digit>           ::= <digit> | #'[a-fA-F]'
+<end-of-file>         ::= #'$'
+```
-- 
2.44.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v1 3/3] codegen: add compiler support to linux aarch64 arch
@ 2024-03-19 19:57 Johnny Richard
  2024-03-19 19:00 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-03-19 19:57 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

This patch adds codegen for aarch64.  If you want compile to aarch64
from a x86_64 machine you have to set the --sysroot and --arch argument
correctly as well (you might want to install gcc for the target
architecture and qemu to run the program).

    $ ./olang examples/main_exit.ol -o main --arch aarch64 --sysroot /usr/aarch64-linux-gnu
    $ qemu-aarch64 ./main
    $ echo $? # should return 0

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/codegen_linux_aaarch64.c | 94 ++++++++++++++++++++++++++++++++++++
 src/codegen_linux_aarch64.h  | 25 ++++++++++
 src/main.c                   |  3 +-
 3 files changed, 121 insertions(+), 1 deletion(-)
 create mode 100644 src/codegen_linux_aaarch64.c
 create mode 100644 src/codegen_linux_aarch64.h

diff --git a/src/codegen_linux_aaarch64.c b/src/codegen_linux_aaarch64.c
new file mode 100644
index 0000000..657a4f4
--- /dev/null
+++ b/src/codegen_linux_aaarch64.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#include <assert.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "codegen_linux_aarch64.h"
+#include "list.h"
+
+#define SYS_exit (93)
+
+/**
+ *  ───────────────────────────────────────────────────────────────────
+ *  Arch/ABI    Instruction           System  Ret  Ret  Error    Notes
+ *                                    call #  val  val2
+ *  ───────────────────────────────────────────────────────────────────
+ *  arm64       svc #0                w8      x0   x1   -
+ *  ──────────────────────────────────────────────────────────────
+ *  Arch/ABI      arg1  arg2  arg3  arg4  arg5  arg6  arg7  Notes
+ *  ──────────────────────────────────────────────────────────────
+ *  arm64         x0    x1    x2    x3    x4    x5    -
+ */
+
+static void
+codegen_linux_aarch64_emit_start_entrypoint(FILE *out);
+
+static void
+codegen_linux_aarch64_emit_function(FILE *out, ast_fn_definition_t *fn);
+
+void
+codegen_linux_aarch64_emit_program(FILE *out, ast_node_t *node)
+{
+    codegen_linux_aarch64_emit_start_entrypoint(out);
+
+    assert(node->kind == AST_NODE_PROGRAM);
+    ast_program_t program = node->data.as_program;
+
+    ast_fn_definition_t fn = program.fn->data.as_fn_def;
+
+    assert(string_view_eq_to_cstr(fn.identifier, "main"));
+    codegen_linux_aarch64_emit_function(out, &fn);
+}
+
+static void
+codegen_linux_aarch64_emit_start_entrypoint(FILE *out)
+{
+    fprintf(out, ".text\n");
+    fprintf(out, ".globl _start\n\n");
+
+    fprintf(out, "_start:\n");
+    fprintf(out, "    bl main\n");
+    fprintf(out, "    mov w8, #%d\n", SYS_exit);
+    fprintf(out, "    svc #0\n");
+}
+
+static void
+codegen_linux_aarch64_emit_function(FILE *out, ast_fn_definition_t *fn)
+{
+    ast_node_t *block_node = fn->block;
+    assert(block_node->kind == AST_NODE_BLOCK);
+    ast_block_t block = block_node->data.as_block;
+
+    assert(list_size(block.nodes) == 1);
+
+    list_item_t *nodes_item = list_get(block.nodes, 0);
+    ast_node_t *return_node = nodes_item->value;
+    assert(return_node->kind == AST_NODE_RETURN_STMT);
+    ast_return_stmt_t return_stmt = return_node->data.as_return_stmt;
+
+    ast_node_t *literal_node = return_stmt.data;
+    assert(literal_node->kind == AST_NODE_LITERAL);
+    ast_literal_t literal_u32 = literal_node->data.as_literal;
+
+    assert(literal_u32.kind == AST_LITERAL_U32);
+    uint32_t exit_code = literal_u32.value.as_u32;
+
+    fprintf(out, "" SV_FMT ":\n", SV_ARG(fn->identifier));
+    fprintf(out, "    mov x0, #%d\n", exit_code);
+    fprintf(out, "    ret\n");
+}
diff --git a/src/codegen_linux_aarch64.h b/src/codegen_linux_aarch64.h
new file mode 100644
index 0000000..fb88b64
--- /dev/null
+++ b/src/codegen_linux_aarch64.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#ifndef CODEGEN_LINUX_AARCH64_H
+#define CODEGEN_LINUX_AARCH64_H
+
+#include "ast.h"
+
+void
+codegen_linux_aarch64_emit_program(FILE *out, ast_node_t *prog);
+
+#endif /* CODEGEN_LINUX_AARCH64_H */
diff --git a/src/main.c b/src/main.c
index 3e1b2a3..ff0aaa8 100644
--- a/src/main.c
+++ b/src/main.c
@@ -23,6 +23,7 @@
 
 #include "arena.h"
 #include "cli.h"
+#include "codegen_linux_aarch64.h"
 #include "codegen_linux_x86_64.h"
 #include "lexer.h"
 #include "parser.h"
@@ -121,7 +122,7 @@ handle_codegen_linux(cli_opts_t *opts)
         if (strcmp(opts->arch, "x86_64") == 0) {
             codegen_linux_x86_64_emit_program(out, ast);
         } else if (strcmp(opts->arch, "aarch64") == 0) {
-            assert(false && "Not implemented yet.");
+            codegen_linux_aarch64_emit_program(out, ast);
         } else {
             fprintf(stderr, "error: architecture '%s' not supported\n", opts->arch);
             cli_print_usage(stderr, opts->compiler_path);
-- 
2.44.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v3 3/3] parser: add all binary operation expressions
@ 2024-03-18  8:39 Johnny Richard
  2024-03-18  7:43 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-03-18  8:39 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

This implementation uses precedence climbing method to guarantee
precedence.

Now is pretty difficult to visualize the ast tree since we don't have a
ast pretty printer.  I have validated it by running **gdb**.

Testing the ast with unit tests is quite annoying without a good "DSL".
We can design such DSL in the near future. =^)

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
v2: Replace the previous solution with precedence based on function
    calls to climbing method. The solution is more extensible and smaller
    since it already maps every possible binary operation at this very
    moment.

 src/lexer.c  |  28 +++++++
 src/lexer.h  |   3 +
 src/parser.c | 209 +++++++++++++++++++++++++++++++++++++++++++++++----
 3 files changed, 227 insertions(+), 13 deletions(-)

diff --git a/src/lexer.c b/src/lexer.c
index 23f0326..801e4d0 100644
--- a/src/lexer.c
+++ b/src/lexer.c
@@ -311,6 +311,34 @@ token_kind_to_cstr(token_kind_t kind)
     return token_kind_str_table[kind];
 }
 
+bool
+token_kind_is_binary_op(token_kind_t kind)
+{
+    switch (kind) {
+        case TOKEN_PLUS:
+        case TOKEN_DASH:
+        case TOKEN_SLASH:
+        case TOKEN_STAR:
+        case TOKEN_PERCENT:
+        case TOKEN_BITWISE_LSHIFT:
+        case TOKEN_BITWISE_RSHIFT:
+        case TOKEN_LT:
+        case TOKEN_CMP_LEQ:
+        case TOKEN_GT:
+        case TOKEN_CMP_GEQ:
+        case TOKEN_CMP_EQ:
+        case TOKEN_CMP_NEQ:
+        case TOKEN_AND:
+        case TOKEN_CIRCUMFLEX:
+        case TOKEN_PIPE:
+        case TOKEN_LOGICAL_AND:
+        case TOKEN_LOGICAL_OR:
+            return true;
+        default:
+            return false;
+    }
+}
+
 static char
 lexer_current_char(lexer_t *lexer)
 {
diff --git a/src/lexer.h b/src/lexer.h
index 5ed777b..80fb25a 100644
--- a/src/lexer.h
+++ b/src/lexer.h
@@ -104,6 +104,9 @@ lexer_lookahead(lexer_t *lexer, token_t *token, size_t n);
 char *
 token_kind_to_cstr(token_kind_t kind);
 
+bool
+token_kind_is_binary_op(token_kind_t kind);
+
 string_view_t
 lexer_get_token_line(lexer_t *lexer, token_t *token);
 
diff --git a/src/parser.c b/src/parser.c
index 5b7d39a..76ef91a 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -39,6 +39,12 @@ parser_parse_block(parser_t *parser);
 ast_node_t *
 parser_parse_fn_definition(parser_t *parser);
 
+static ast_node_t *
+parser_parse_expr(parser_t *parser);
+
+static ast_node_t *
+parser_parse_factor(parser_t *parser);
+
 static void
 skip_line_feeds(lexer_t *lexer);
 
@@ -57,10 +63,189 @@ ast_node_t *
 parser_parse_program(parser_t *parser)
 {
     ast_node_t *fn = parser_parse_fn_definition(parser);
+    if (fn == NULL) {
+        return NULL;
+    }
 
     return ast_new_program(parser->arena, fn);
 }
 
+static ast_binary_op_kind_t
+token_kind_to_binary_op_kind(token_kind_t kind)
+{
+    switch (kind) {
+        case TOKEN_PLUS:
+            return AST_BINOP_ADDITION;
+        case TOKEN_DASH:
+            return AST_BINOP_SUBTRACTION;
+        case TOKEN_SLASH:
+            return AST_BINOP_DIVISION;
+        case TOKEN_STAR:
+            return AST_BINOP_MULTIPLICATION;
+        case TOKEN_PERCENT:
+            return AST_BINOP_REMINDER;
+        case TOKEN_BITWISE_LSHIFT:
+            return AST_BINOP_BITWISE_LSHIFT;
+        case TOKEN_BITWISE_RSHIFT:
+            return AST_BINOP_BITWISE_RSHIFT;
+        case TOKEN_CIRCUMFLEX:
+            return AST_BINOP_BITWISE_XOR;
+        case TOKEN_AND:
+            return AST_BINOP_BITWISE_AND;
+        case TOKEN_PIPE:
+            return AST_BINOP_BITWISE_OR;
+        case TOKEN_LT:
+            return AST_BINOP_CMP_LT;
+        case TOKEN_GT:
+            return AST_BINOP_CMP_GT;
+        case TOKEN_CMP_LEQ:
+            return AST_BINOP_CMP_LEQ;
+        case TOKEN_CMP_GEQ:
+            return AST_BINOP_CMP_GEQ;
+        case TOKEN_CMP_EQ:
+            return AST_BINOP_CMP_EQ;
+        case TOKEN_CMP_NEQ:
+            return AST_BINOP_CMP_NEQ;
+        case TOKEN_LOGICAL_AND:
+            return AST_BINOP_LOGICAL_AND;
+        case TOKEN_LOGICAL_OR:
+            return AST_BINOP_LOGICAL_OR;
+        default: {
+            fprintf(stderr, "error: token kind (%s) not compatible with binary op kind\n", token_kind_to_cstr(kind));
+            assert(false);
+        }
+    }
+}
+
+typedef enum
+{
+    BINOP_MIN_PREC,
+    BINOP_LOGICAL_OR_PREC,
+    BINOP_LOGICAL_AND_PREC,
+    BINOP_BITWISE_OR_PREC,
+    BINOP_BITWISE_XOR_PREC,
+    BINOP_BITWISE_AND_PREC,
+    BINOP_CMP_EQ_AND_NEQ_PREC,
+    BINOP_CMP_LT_AND_GT_PREC,
+    BINOP_BITWISE_SHIFT_PREC,
+    BINOP_ADDITIVE_PREC,
+    BINOP_MULTIPLICATIVE_PREC,
+} binary_op_precedence_t;
+
+static binary_op_precedence_t
+get_binary_op_precedence(token_kind_t kind)
+{
+    switch (kind) {
+        case TOKEN_PLUS:
+        case TOKEN_DASH:
+            return BINOP_ADDITIVE_PREC;
+        case TOKEN_SLASH:
+        case TOKEN_STAR:
+        case TOKEN_PERCENT:
+            return BINOP_MULTIPLICATIVE_PREC;
+        case TOKEN_BITWISE_LSHIFT:
+        case TOKEN_BITWISE_RSHIFT:
+            return BINOP_BITWISE_SHIFT_PREC;
+        case TOKEN_LT:
+        case TOKEN_GT:
+            return BINOP_CMP_LT_AND_GT_PREC;
+        case TOKEN_EQ:
+        case TOKEN_CMP_NEQ:
+            return BINOP_CMP_EQ_AND_NEQ_PREC;
+        case TOKEN_AND:
+            return BINOP_BITWISE_AND_PREC;
+        case TOKEN_CIRCUMFLEX:
+            return BINOP_BITWISE_XOR_PREC;
+        case TOKEN_PIPE:
+            return BINOP_BITWISE_OR_PREC;
+        case TOKEN_LOGICAL_AND:
+            return BINOP_LOGICAL_AND_PREC;
+        case TOKEN_LOGICAL_OR:
+            return BINOP_LOGICAL_OR_PREC;
+        default:
+            assert(false);
+    }
+}
+
+static ast_node_t *
+parser_parse_expr_1(parser_t *parser, ast_node_t *lhs, size_t prev_precedence)
+{
+    ast_node_t *expr = NULL;
+
+    token_t lookahead_token;
+    lexer_peek_next(parser->lexer, &lookahead_token);
+
+    while (token_kind_is_binary_op(lookahead_token.kind) &&
+           get_binary_op_precedence(lookahead_token.kind) >= prev_precedence) {
+        token_t token_op;
+        lexer_next_token(parser->lexer, &token_op);
+
+        ast_node_t *rhs = parser_parse_factor(parser);
+        if (rhs == NULL) {
+            return NULL;
+        }
+
+        lexer_peek_next(parser->lexer, &lookahead_token);
+
+        while (token_kind_is_binary_op(lookahead_token.kind) &&
+               get_binary_op_precedence(lookahead_token.kind) > get_binary_op_precedence(token_op.kind)) {
+            rhs = parser_parse_expr_1(parser, rhs, get_binary_op_precedence(token_op.kind));
+            lexer_peek_next(parser->lexer, &lookahead_token);
+        }
+
+        expr = ast_new_node_bin_op(parser->arena, token_kind_to_binary_op_kind(token_op.kind), lhs, rhs);
+        if (expr == NULL) {
+            return NULL;
+        }
+    }
+
+    if (expr == NULL) {
+        return lhs;
+    }
+
+    return expr;
+}
+
+static ast_node_t *
+parser_parse_expr(parser_t *parser)
+{
+    ast_node_t *lhs = parser_parse_factor(parser);
+    if (lhs == NULL) {
+        return NULL;
+    }
+
+    return parser_parse_expr_1(parser, lhs, BINOP_MIN_PREC);
+}
+
+static ast_node_t *
+parser_parse_factor(parser_t *parser)
+{
+    token_t token;
+    lexer_next_token(parser->lexer, &token);
+
+    switch (token.kind) {
+        case TOKEN_NUMBER:
+            return ast_new_node_literal_u32(parser->arena, string_view_to_u32(token.value));
+
+        case TOKEN_OPAREN: {
+            ast_node_t *expr = parser_parse_expr(parser);
+            if (expr == NULL) {
+                return NULL;
+            }
+
+            if (!skip_expected_token(parser, TOKEN_CPAREN)) {
+                return NULL;
+            }
+
+            return expr;
+        }
+        default: {
+            fprintf(stderr, "error: parse_factor: unsupported or invalid token (%s)\n", token_kind_to_cstr(token.kind));
+            assert(false);
+        }
+    }
+}
+
 ast_node_t *
 parser_parse_fn_definition(parser_t *parser)
 {
@@ -131,41 +316,39 @@ parser_parse_type(parser_t *parser, type_t *type)
 static ast_node_t *
 parser_parse_block(parser_t *parser)
 {
-    token_t number_token;
     if (!skip_expected_token(parser, TOKEN_OCURLY)) {
-        return false;
+        return NULL;
     }
 
     skip_line_feeds(parser->lexer);
 
     ast_node_t *node_block = ast_new_node_block(parser->arena);
+    if (node_block == NULL) {
+        return NULL;
+    }
 
     if (!skip_expected_token(parser, TOKEN_RETURN)) {
-        return false;
+        return NULL;
     }
 
     ast_node_t *node_return_stmt = ast_new_node_return_stmt(parser->arena);
     assert(node_return_stmt);
 
-    if (!expected_token(parser, &number_token, TOKEN_NUMBER)) {
-        return false;
+    ast_node_t *expr = parser_parse_expr(parser);
+    if (expr == NULL) {
+        return NULL;
     }
 
-    ast_node_t *literal_node = ast_new_node_literal_u32(parser->arena, string_view_to_u32(number_token.value));
-    assert(literal_node);
-
-    node_return_stmt->data.as_return_stmt.data = literal_node;
+    node_return_stmt->data.as_return_stmt.data = expr;
 
     list_append(node_block->data.as_block.nodes, node_return_stmt);
-
     if (!skip_expected_token(parser, TOKEN_LF)) {
-        return false;
+        return NULL;
     }
 
     skip_line_feeds(parser->lexer);
-
     if (!skip_expected_token(parser, TOKEN_CCURLY)) {
-        return false;
+        return NULL;
     }
 
     return node_block;
-- 
2.44.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v2 3/3] parser: add all binary operation expressions
@ 2024-03-17 21:29 Johnny Richard
  2024-03-17 20:37 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-03-17 21:29 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

This implementation uses precedence climbing method to guarantee
precedence.

Now is pretty difficult to visualize the ast tree since we don't have a
ast pretty printer.  I have validated it by running **gdb**.

Testing the ast with unit tests is quite annoying without a good "DSL".
We can design such DSL in the near future. =^)

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
v2: Replace the previous solution with precedence based on function
    calls to climbing method. The solution is more extensible and smaller
    since it already maps every possible binary operation at this very
    moment.

 src/lexer.c  |  28 +++++++
 src/lexer.h  |   3 +
 src/parser.c | 209 +++++++++++++++++++++++++++++++++++++++++++++++----
 3 files changed, 227 insertions(+), 13 deletions(-)

diff --git a/src/lexer.c b/src/lexer.c
index 14c2962..437d0b1 100644
--- a/src/lexer.c
+++ b/src/lexer.c
@@ -322,6 +322,34 @@ token_kind_to_cstr(token_kind_t kind)
     return token_kind_str_table[kind];
 }
 
+bool
+token_kind_is_binary_op(token_kind_t kind)
+{
+    switch (kind) {
+        case TOKEN_PLUS:
+        case TOKEN_DASH:
+        case TOKEN_SLASH:
+        case TOKEN_STAR:
+        case TOKEN_PERCENT:
+        case TOKEN_BITWISE_LSHIFT:
+        case TOKEN_BITWISE_RSHIFT:
+        case TOKEN_LT:
+        case TOKEN_CMP_LEQ:
+        case TOKEN_GT:
+        case TOKEN_CMP_GEQ:
+        case TOKEN_CMP_EQ:
+        case TOKEN_CMP_NEQ:
+        case TOKEN_AND:
+        case TOKEN_CIRCUMFLEX:
+        case TOKEN_PIPE:
+        case TOKEN_LOGICAL_AND:
+        case TOKEN_LOGICAL_OR:
+            return true;
+        default:
+            return false;
+    }
+}
+
 static char
 lexer_current_char(lexer_t *lexer)
 {
diff --git a/src/lexer.h b/src/lexer.h
index 5ed777b..80fb25a 100644
--- a/src/lexer.h
+++ b/src/lexer.h
@@ -104,6 +104,9 @@ lexer_lookahead(lexer_t *lexer, token_t *token, size_t n);
 char *
 token_kind_to_cstr(token_kind_t kind);
 
+bool
+token_kind_is_binary_op(token_kind_t kind);
+
 string_view_t
 lexer_get_token_line(lexer_t *lexer, token_t *token);
 
diff --git a/src/parser.c b/src/parser.c
index 5b7d39a..76ef91a 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -39,6 +39,12 @@ parser_parse_block(parser_t *parser);
 ast_node_t *
 parser_parse_fn_definition(parser_t *parser);
 
+static ast_node_t *
+parser_parse_expr(parser_t *parser);
+
+static ast_node_t *
+parser_parse_factor(parser_t *parser);
+
 static void
 skip_line_feeds(lexer_t *lexer);
 
@@ -57,10 +63,189 @@ ast_node_t *
 parser_parse_program(parser_t *parser)
 {
     ast_node_t *fn = parser_parse_fn_definition(parser);
+    if (fn == NULL) {
+        return NULL;
+    }
 
     return ast_new_program(parser->arena, fn);
 }
 
+static ast_binary_op_kind_t
+token_kind_to_binary_op_kind(token_kind_t kind)
+{
+    switch (kind) {
+        case TOKEN_PLUS:
+            return AST_BINOP_ADDITION;
+        case TOKEN_DASH:
+            return AST_BINOP_SUBTRACTION;
+        case TOKEN_SLASH:
+            return AST_BINOP_DIVISION;
+        case TOKEN_STAR:
+            return AST_BINOP_MULTIPLICATION;
+        case TOKEN_PERCENT:
+            return AST_BINOP_REMINDER;
+        case TOKEN_BITWISE_LSHIFT:
+            return AST_BINOP_BITWISE_LSHIFT;
+        case TOKEN_BITWISE_RSHIFT:
+            return AST_BINOP_BITWISE_RSHIFT;
+        case TOKEN_CIRCUMFLEX:
+            return AST_BINOP_BITWISE_XOR;
+        case TOKEN_AND:
+            return AST_BINOP_BITWISE_AND;
+        case TOKEN_PIPE:
+            return AST_BINOP_BITWISE_OR;
+        case TOKEN_LT:
+            return AST_BINOP_CMP_LT;
+        case TOKEN_GT:
+            return AST_BINOP_CMP_GT;
+        case TOKEN_CMP_LEQ:
+            return AST_BINOP_CMP_LEQ;
+        case TOKEN_CMP_GEQ:
+            return AST_BINOP_CMP_GEQ;
+        case TOKEN_CMP_EQ:
+            return AST_BINOP_CMP_EQ;
+        case TOKEN_CMP_NEQ:
+            return AST_BINOP_CMP_NEQ;
+        case TOKEN_LOGICAL_AND:
+            return AST_BINOP_LOGICAL_AND;
+        case TOKEN_LOGICAL_OR:
+            return AST_BINOP_LOGICAL_OR;
+        default: {
+            fprintf(stderr, "error: token kind (%s) not compatible with binary op kind\n", token_kind_to_cstr(kind));
+            assert(false);
+        }
+    }
+}
+
+typedef enum
+{
+    BINOP_MIN_PREC,
+    BINOP_LOGICAL_OR_PREC,
+    BINOP_LOGICAL_AND_PREC,
+    BINOP_BITWISE_OR_PREC,
+    BINOP_BITWISE_XOR_PREC,
+    BINOP_BITWISE_AND_PREC,
+    BINOP_CMP_EQ_AND_NEQ_PREC,
+    BINOP_CMP_LT_AND_GT_PREC,
+    BINOP_BITWISE_SHIFT_PREC,
+    BINOP_ADDITIVE_PREC,
+    BINOP_MULTIPLICATIVE_PREC,
+} binary_op_precedence_t;
+
+static binary_op_precedence_t
+get_binary_op_precedence(token_kind_t kind)
+{
+    switch (kind) {
+        case TOKEN_PLUS:
+        case TOKEN_DASH:
+            return BINOP_ADDITIVE_PREC;
+        case TOKEN_SLASH:
+        case TOKEN_STAR:
+        case TOKEN_PERCENT:
+            return BINOP_MULTIPLICATIVE_PREC;
+        case TOKEN_BITWISE_LSHIFT:
+        case TOKEN_BITWISE_RSHIFT:
+            return BINOP_BITWISE_SHIFT_PREC;
+        case TOKEN_LT:
+        case TOKEN_GT:
+            return BINOP_CMP_LT_AND_GT_PREC;
+        case TOKEN_EQ:
+        case TOKEN_CMP_NEQ:
+            return BINOP_CMP_EQ_AND_NEQ_PREC;
+        case TOKEN_AND:
+            return BINOP_BITWISE_AND_PREC;
+        case TOKEN_CIRCUMFLEX:
+            return BINOP_BITWISE_XOR_PREC;
+        case TOKEN_PIPE:
+            return BINOP_BITWISE_OR_PREC;
+        case TOKEN_LOGICAL_AND:
+            return BINOP_LOGICAL_AND_PREC;
+        case TOKEN_LOGICAL_OR:
+            return BINOP_LOGICAL_OR_PREC;
+        default:
+            assert(false);
+    }
+}
+
+static ast_node_t *
+parser_parse_expr_1(parser_t *parser, ast_node_t *lhs, size_t prev_precedence)
+{
+    ast_node_t *expr = NULL;
+
+    token_t lookahead_token;
+    lexer_peek_next(parser->lexer, &lookahead_token);
+
+    while (token_kind_is_binary_op(lookahead_token.kind) &&
+           get_binary_op_precedence(lookahead_token.kind) >= prev_precedence) {
+        token_t token_op;
+        lexer_next_token(parser->lexer, &token_op);
+
+        ast_node_t *rhs = parser_parse_factor(parser);
+        if (rhs == NULL) {
+            return NULL;
+        }
+
+        lexer_peek_next(parser->lexer, &lookahead_token);
+
+        while (token_kind_is_binary_op(lookahead_token.kind) &&
+               get_binary_op_precedence(lookahead_token.kind) > get_binary_op_precedence(token_op.kind)) {
+            rhs = parser_parse_expr_1(parser, rhs, get_binary_op_precedence(token_op.kind));
+            lexer_peek_next(parser->lexer, &lookahead_token);
+        }
+
+        expr = ast_new_node_bin_op(parser->arena, token_kind_to_binary_op_kind(token_op.kind), lhs, rhs);
+        if (expr == NULL) {
+            return NULL;
+        }
+    }
+
+    if (expr == NULL) {
+        return lhs;
+    }
+
+    return expr;
+}
+
+static ast_node_t *
+parser_parse_expr(parser_t *parser)
+{
+    ast_node_t *lhs = parser_parse_factor(parser);
+    if (lhs == NULL) {
+        return NULL;
+    }
+
+    return parser_parse_expr_1(parser, lhs, BINOP_MIN_PREC);
+}
+
+static ast_node_t *
+parser_parse_factor(parser_t *parser)
+{
+    token_t token;
+    lexer_next_token(parser->lexer, &token);
+
+    switch (token.kind) {
+        case TOKEN_NUMBER:
+            return ast_new_node_literal_u32(parser->arena, string_view_to_u32(token.value));
+
+        case TOKEN_OPAREN: {
+            ast_node_t *expr = parser_parse_expr(parser);
+            if (expr == NULL) {
+                return NULL;
+            }
+
+            if (!skip_expected_token(parser, TOKEN_CPAREN)) {
+                return NULL;
+            }
+
+            return expr;
+        }
+        default: {
+            fprintf(stderr, "error: parse_factor: unsupported or invalid token (%s)\n", token_kind_to_cstr(token.kind));
+            assert(false);
+        }
+    }
+}
+
 ast_node_t *
 parser_parse_fn_definition(parser_t *parser)
 {
@@ -131,41 +316,39 @@ parser_parse_type(parser_t *parser, type_t *type)
 static ast_node_t *
 parser_parse_block(parser_t *parser)
 {
-    token_t number_token;
     if (!skip_expected_token(parser, TOKEN_OCURLY)) {
-        return false;
+        return NULL;
     }
 
     skip_line_feeds(parser->lexer);
 
     ast_node_t *node_block = ast_new_node_block(parser->arena);
+    if (node_block == NULL) {
+        return NULL;
+    }
 
     if (!skip_expected_token(parser, TOKEN_RETURN)) {
-        return false;
+        return NULL;
     }
 
     ast_node_t *node_return_stmt = ast_new_node_return_stmt(parser->arena);
     assert(node_return_stmt);
 
-    if (!expected_token(parser, &number_token, TOKEN_NUMBER)) {
-        return false;
+    ast_node_t *expr = parser_parse_expr(parser);
+    if (expr == NULL) {
+        return NULL;
     }
 
-    ast_node_t *literal_node = ast_new_node_literal_u32(parser->arena, string_view_to_u32(number_token.value));
-    assert(literal_node);
-
-    node_return_stmt->data.as_return_stmt.data = literal_node;
+    node_return_stmt->data.as_return_stmt.data = expr;
 
     list_append(node_block->data.as_block.nodes, node_return_stmt);
-
     if (!skip_expected_token(parser, TOKEN_LF)) {
-        return false;
+        return NULL;
     }
 
     skip_line_feeds(parser->lexer);
-
     if (!skip_expected_token(parser, TOKEN_CCURLY)) {
-        return false;
+        return NULL;
     }
 
     return node_block;
-- 
2.44.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v1 3/3] parser: add basic arithmetic expressions '+' '*' '/' '-'
@ 2024-03-13 21:21 Johnny Richard
  2024-03-13 20:29 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-03-13 21:21 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

This implementation uses a call chain to guarantee calling precedence.
Whenever we try to add more expression types and it becomes complexer,
we will design a solution based on priority.

Now is pretty difficult to visualize the ast tree since we don't have a
ast pretty printer.  I have validated it by running **gdb**.

Testing the ast with unit tests is quite annoying without a good "DSL".
We can design such DSL in the near future. =^)

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/parser.c | 140 ++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 127 insertions(+), 13 deletions(-)

diff --git a/src/parser.c b/src/parser.c
index 5b7d39a..ab7051d 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -39,6 +39,15 @@ parser_parse_block(parser_t *parser);
 ast_node_t *
 parser_parse_fn_definition(parser_t *parser);
 
+static ast_node_t *
+parser_parse_expr(parser_t *parser);
+
+static ast_node_t *
+parser_parse_term(parser_t *parser);
+
+static ast_node_t *
+parser_parse_factor(parser_t *parser);
+
 static void
 skip_line_feeds(lexer_t *lexer);
 
@@ -57,10 +66,117 @@ ast_node_t *
 parser_parse_program(parser_t *parser)
 {
     ast_node_t *fn = parser_parse_fn_definition(parser);
+    if (fn == NULL) {
+        return NULL;
+    }
 
     return ast_new_program(parser->arena, fn);
 }
 
+static ast_binary_op_kind_t
+token_kind_to_binary_op_kind(token_kind_t kind)
+{
+    switch (kind) {
+        case TOKEN_PLUS:
+            return AST_BINOP_ADDITION;
+        case TOKEN_DASH:
+            return AST_BINOP_SUBTRACTION;
+        case TOKEN_SLASH:
+            return AST_BINOP_DIVISION;
+        case TOKEN_STAR:
+            return AST_BINOP_MULTIPLICATION;
+        default: {
+            fprintf(stderr, "error: token kind (%s) not compatible with binary op kind\n", token_kind_to_cstr(kind));
+            assert(false);
+        }
+    }
+}
+
+static ast_node_t *
+parser_parse_expr(parser_t *parser)
+{
+    ast_node_t *node = parser_parse_term(parser);
+    if (node == NULL) {
+        return NULL;
+    }
+
+    token_t token;
+    lexer_peek_next(parser->lexer, &token);
+
+    while (token.kind == TOKEN_PLUS || token.kind == TOKEN_DASH) {
+        lexer_next_token(parser->lexer, &token);
+
+        ast_node_t *lhs = node;
+        ast_node_t *rhs = parser_parse_term(parser);
+        if (rhs == NULL) {
+            return NULL;
+        }
+
+        node = ast_new_node_bin_op(parser->arena, token_kind_to_binary_op_kind(token.kind), lhs, rhs);
+
+        lexer_peek_next(parser->lexer, &token);
+    }
+
+    return node;
+}
+
+static ast_node_t *
+parser_parse_term(parser_t *parser)
+{
+    ast_node_t *node = parser_parse_factor(parser);
+    if (node == NULL) {
+        return NULL;
+    }
+
+    token_t token;
+    lexer_peek_next(parser->lexer, &token);
+
+    while (token.kind == TOKEN_STAR || token.kind == TOKEN_SLASH) {
+        lexer_next_token(parser->lexer, &token);
+
+        ast_node_t *lhs = node;
+        ast_node_t *rhs = parser_parse_factor(parser);
+        if (rhs == NULL) {
+            return NULL;
+        }
+
+        node = ast_new_node_bin_op(parser->arena, token_kind_to_binary_op_kind(token.kind), lhs, rhs);
+
+        lexer_peek_next(parser->lexer, &token);
+    }
+
+    return node;
+}
+
+static ast_node_t *
+parser_parse_factor(parser_t *parser)
+{
+    token_t token;
+    lexer_next_token(parser->lexer, &token);
+
+    switch (token.kind) {
+        case TOKEN_NUMBER:
+            return ast_new_node_literal_u32(parser->arena, string_view_to_u32(token.value));
+
+        case TOKEN_OPAREN: {
+            ast_node_t *expr = parser_parse_expr(parser);
+            if (expr == NULL) {
+                return NULL;
+            }
+
+            if (!skip_expected_token(parser, TOKEN_CPAREN)) {
+                return NULL;
+            }
+
+            return expr;
+        }
+        default: {
+            fprintf(stderr, "error: parse_factor: unsupported or invalid token (%s)\n", token_kind_to_cstr(token.kind));
+            assert(false);
+        }
+    }
+}
+
 ast_node_t *
 parser_parse_fn_definition(parser_t *parser)
 {
@@ -131,41 +247,39 @@ parser_parse_type(parser_t *parser, type_t *type)
 static ast_node_t *
 parser_parse_block(parser_t *parser)
 {
-    token_t number_token;
     if (!skip_expected_token(parser, TOKEN_OCURLY)) {
-        return false;
+        return NULL;
     }
 
     skip_line_feeds(parser->lexer);
 
     ast_node_t *node_block = ast_new_node_block(parser->arena);
+    if (node_block == NULL) {
+        return NULL;
+    }
 
     if (!skip_expected_token(parser, TOKEN_RETURN)) {
-        return false;
+        return NULL;
     }
 
     ast_node_t *node_return_stmt = ast_new_node_return_stmt(parser->arena);
     assert(node_return_stmt);
 
-    if (!expected_token(parser, &number_token, TOKEN_NUMBER)) {
-        return false;
+    ast_node_t *expr = parser_parse_expr(parser);
+    if (expr == NULL) {
+        return NULL;
     }
 
-    ast_node_t *literal_node = ast_new_node_literal_u32(parser->arena, string_view_to_u32(number_token.value));
-    assert(literal_node);
-
-    node_return_stmt->data.as_return_stmt.data = literal_node;
+    node_return_stmt->data.as_return_stmt.data = expr;
 
     list_append(node_block->data.as_block.nodes, node_return_stmt);
-
     if (!skip_expected_token(parser, TOKEN_LF)) {
-        return false;
+        return NULL;
     }
 
     skip_line_feeds(parser->lexer);
-
     if (!skip_expected_token(parser, TOKEN_CCURLY)) {
-        return false;
+        return NULL;
     }
 
     return node_block;
-- 
2.44.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v3] refactor: rename zero programming language to olang
@ 2024-03-13 12:44 Fabio Maciel
  2024-03-13 12:45 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Fabio Maciel @ 2024-03-13 12:44 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard, Maria Ruy, Fabio Maciel

From: Johnny Richard <johnny@johnnyrichard.com>

After a long discussion we've decided to rename the project to olang.

This patch removes anything related to zero programming language and
also renames the compiler and extension.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
Co-authored-by: Maria Ruy <maria.dev456@gmail.com>
Co-authored-by: Fabio Maciel<fabio@fabiomaciel.com>
---
 V4 fix docs/Makefile
 .gitignore                             |  2 +-
 Makefile                               |  4 ++--
 docs/Makefile                          |  2 +-
 docs/index.md                          |  2 +-
 docs/manpages/{0c.md => olang.md}      |  8 +++----
 docs/pages/hacking.md                  |  2 +-
 docs/template.html                     |  2 +-
 examples/{main_exit.0 => main_exit.ol} |  0
 tests/integration/cli_runner.c         |  6 ++---
 tests/integration/cli_test.c           | 32 +++++++++++++-------------
 10 files changed, 30 insertions(+), 30 deletions(-)
 rename docs/manpages/{0c.md => olang.md} (74%)
 rename examples/{main_exit.0 => main_exit.ol} (100%)

diff --git a/.gitignore b/.gitignore
index fc7d161..a565aae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
-0c
+olang
 build
 *.o
 docs/site.tar.gz
diff --git a/Makefile b/Makefile
index 662d039..cdfc8e1 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-TARGET    := 0c
+TARGET    := olang
 SRC_DIR   := src
 BUILD_DIR := build
 CFLAGS    := -Werror -Wall -Wextra -Wmissing-declarations -pedantic -std=c11 -ggdb
@@ -42,7 +42,7 @@ unit-test:
 clean:
 	$(MAKE) -C tests/integration/ clean
 	$(MAKE) -C tests/unit/ clean
-	@rm -rf build/ 0c
+	@rm -rf build/ $(TARGET)
 
 .PHONY: check
 check:
diff --git a/docs/Makefile b/docs/Makefile
index 54561a1..c890eca 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -21,7 +21,7 @@ clean:
 dist: $(DIST_FILE)
 
 .PHONY: manpages
-manpages: $(BUILD_DIR) $(MANPAGES)/0c.1
+manpages: $(BUILD_DIR) $(MANPAGES)/olang.1
 
 $(MANPAGES)/%.1: manpages/%.md
 	$(PANDOC) -s -t man $< > $@
diff --git a/docs/index.md b/docs/index.md
index b6d5c1a..1a28069 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,6 +1,6 @@
 % Welcome to olang documentation
 
-The zero programming language.
+The O Programming Language.
 
 ## olang manifest
 
diff --git a/docs/manpages/0c.md b/docs/manpages/olang.md
similarity index 74%
rename from docs/manpages/0c.md
rename to docs/manpages/olang.md
index e3d3cfc..c54c597 100644
--- a/docs/manpages/0c.md
+++ b/docs/manpages/olang.md
@@ -1,21 +1,21 @@
-% 0C(1)
+% OLANG(1)
 % olang mantainers
 % Feb 2024
 
 # NAME
 
-0c - zero language compiler
+olang - O Programming Language compiler
 
 # SYNOPSIS
 
-**0c**
+**olang**
     source_file
     [**----dump-tokens**]
     [**--o** output_file [**----save-temps**]] 
 
 # DESCRIPTION
 
-**0c** is the offical compiler for zero language, it is also a tool that
+**olang** is the offical O programming language compiler, it is also a tool that
 contains utilities to help the language development.
 
 # GENERAL OPTIONS
diff --git a/docs/pages/hacking.md b/docs/pages/hacking.md
index ef88791..fe8f705 100644
--- a/docs/pages/hacking.md
+++ b/docs/pages/hacking.md
@@ -53,7 +53,7 @@ Testing
 -------
 
 There are two layers of tests **integration** and **unit**. The
-integration test is going to execute the **0c** compiler and check if
+integration test is going to execute the **olang** compiler and check if
 the generated binary acts as expected. Unit tests will test C functions.
 
 For both unit and integration we use **munit** framework:
diff --git a/docs/template.html b/docs/template.html
index ecc92a2..4e066d1 100644
--- a/docs/template.html
+++ b/docs/template.html
@@ -52,7 +52,7 @@
 </head>
 <body>
   <header>
-    <h1>∅lang | The zero programming language</h1>
+    <h1>olang | The O Programming Language</h1>
     <nav>
       <a href="/">Index</a> |
       <a href="/pages/getting-started.html">Getting started (WIP)</a> |
diff --git a/examples/main_exit.0 b/examples/main_exit.ol
similarity index 100%
rename from examples/main_exit.0
rename to examples/main_exit.ol
diff --git a/tests/integration/cli_runner.c b/tests/integration/cli_runner.c
index fed12ab..636abfc 100644
--- a/tests/integration/cli_runner.c
+++ b/tests/integration/cli_runner.c
@@ -24,7 +24,7 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
-#define OLANG_COMPILER_PATH "../../0c"
+#define OLANG_COMPILER_PATH "../../olang"
 
 static int compiler_exists_already_checked = 0;
 
@@ -83,7 +83,7 @@ cli_runner_compiler_dump_tokens(char *src)
 {
     cli_result_t result = { 0 };
 
-    char *program_args[] = { "0c", "--dump-tokens", src, NULL };
+    char *program_args[] = { "olang", "--dump-tokens", src, NULL };
     cli_runner_compiler(&result, program_args);
     return result;
 }
@@ -94,7 +94,7 @@ cli_runner_compiler_compile(char *src)
     cli_result_t result = { 0 };
     create_tmp_file_name(result.binary_path);
 
-    char *program_args[] = { "0c", src, "-o", result.binary_path, NULL };
+    char *program_args[] = { "olang", src, "-o", result.binary_path, NULL };
     cli_runner_compiler(&result, program_args);
     return result;
 }
diff --git a/tests/integration/cli_test.c b/tests/integration/cli_test.c
index c5896df..8cc22f9 100644
--- a/tests/integration/cli_test.c
+++ b/tests/integration/cli_test.c
@@ -22,30 +22,30 @@
 static MunitResult
 test_cli_dump_tokens(const MunitParameter params[], void *user_data_or_fixture)
 {
-    cli_result_t compilation_result = cli_runner_compiler_dump_tokens("../../examples/main_exit.0");
+    cli_result_t compilation_result = cli_runner_compiler_dump_tokens("../../examples/main_exit.ol");
     munit_assert_int(compilation_result.exec.exit_code, ==, 0);
     munit_assert_string_equal(compilation_result.exec.stdout_buf,
-                              "../../examples/main_exit.0:1:1: <fn>\n"
-                              "../../examples/main_exit.0:1:4: <identifier>\n"
-                              "../../examples/main_exit.0:1:8: <(>\n"
-                              "../../examples/main_exit.0:1:9: <)>\n"
-                              "../../examples/main_exit.0:1:10: <:>\n"
-                              "../../examples/main_exit.0:1:12: <identifier>\n"
-                              "../../examples/main_exit.0:1:16: <{>\n"
-                              "../../examples/main_exit.0:1:17: <line_feed>\n"
-                              "../../examples/main_exit.0:2:3: <return>\n"
-                              "../../examples/main_exit.0:2:10: <number>\n"
-                              "../../examples/main_exit.0:2:11: <line_feed>\n"
-                              "../../examples/main_exit.0:3:1: <}>\n"
-                              "../../examples/main_exit.0:3:2: <line_feed>\n"
-                              "../../examples/main_exit.0:4:1: <EOF>\n");
+                              "../../examples/main_exit.ol:1:1: <fn>\n"
+                              "../../examples/main_exit.ol:1:4: <identifier>\n"
+                              "../../examples/main_exit.ol:1:8: <(>\n"
+                              "../../examples/main_exit.ol:1:9: <)>\n"
+                              "../../examples/main_exit.ol:1:10: <:>\n"
+                              "../../examples/main_exit.ol:1:12: <identifier>\n"
+                              "../../examples/main_exit.ol:1:16: <{>\n"
+                              "../../examples/main_exit.ol:1:17: <line_feed>\n"
+                              "../../examples/main_exit.ol:2:3: <return>\n"
+                              "../../examples/main_exit.ol:2:10: <number>\n"
+                              "../../examples/main_exit.ol:2:11: <line_feed>\n"
+                              "../../examples/main_exit.ol:3:1: <}>\n"
+                              "../../examples/main_exit.ol:3:2: <line_feed>\n"
+                              "../../examples/main_exit.ol:4:1: <EOF>\n");
     return MUNIT_OK;
 }
 
 static MunitResult
 test_cli_compile_minimal_program(const MunitParameter params[], void *user_data_or_fixture)
 {
-    cli_result_t compilation_result = cli_runner_compiler_compile("../../examples/main_exit.0");
+    cli_result_t compilation_result = cli_runner_compiler_compile("../../examples/main_exit.ol");
     munit_assert_int(compilation_result.exec.exit_code, ==, 0);
 
     char *command_args[] = { compilation_result.binary_path, NULL };
-- 
2.39.3 (Apple Git-145)


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v1] refactor: rename zero programming language to olang
@ 2024-03-12 19:35 Johnny Richard
  2024-03-12 18:40 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-03-12 19:35 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

After a long discussion we've decided to rename the project to olang.

This patch removes anything related to zero programming language and
also renames the compiler and extension.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 .gitignore                             |  2 +-
 Makefile                               |  4 ++--
 docs/Makefile                          |  2 +-
 docs/index.md                          |  2 +-
 docs/manpages/{0c.md => oc.md}         |  8 +++----
 docs/pages/hacking.md                  |  2 +-
 docs/template.html                     |  2 +-
 examples/{main_exit.0 => main_exit.ol} |  0
 tests/integration/cli_runner.c         |  6 ++---
 tests/integration/cli_test.c           | 32 +++++++++++++-------------
 10 files changed, 30 insertions(+), 30 deletions(-)
 rename docs/manpages/{0c.md => oc.md} (76%)
 rename examples/{main_exit.0 => main_exit.ol} (100%)

diff --git a/.gitignore b/.gitignore
index fc7d161..941bf48 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
-0c
+oc
 build
 *.o
 docs/site.tar.gz
diff --git a/Makefile b/Makefile
index 662d039..2bff8a7 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-TARGET    := 0c
+TARGET    := oc
 SRC_DIR   := src
 BUILD_DIR := build
 CFLAGS    := -Werror -Wall -Wextra -Wmissing-declarations -pedantic -std=c11 -ggdb
@@ -42,7 +42,7 @@ unit-test:
 clean:
 	$(MAKE) -C tests/integration/ clean
 	$(MAKE) -C tests/unit/ clean
-	@rm -rf build/ 0c
+	@rm -rf build/ $(TARGET)
 
 .PHONY: check
 check:
diff --git a/docs/Makefile b/docs/Makefile
index 54561a1..d34f9f5 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -21,7 +21,7 @@ clean:
 dist: $(DIST_FILE)
 
 .PHONY: manpages
-manpages: $(BUILD_DIR) $(MANPAGES)/0c.1
+manpages: $(BUILD_DIR) $(MANPAGES)/oc.1
 
 $(MANPAGES)/%.1: manpages/%.md
 	$(PANDOC) -s -t man $< > $@
diff --git a/docs/index.md b/docs/index.md
index b6d5c1a..1a28069 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,6 +1,6 @@
 % Welcome to olang documentation
 
-The zero programming language.
+The O Programming Language.
 
 ## olang manifest
 
diff --git a/docs/manpages/0c.md b/docs/manpages/oc.md
similarity index 76%
rename from docs/manpages/0c.md
rename to docs/manpages/oc.md
index e3d3cfc..e4b263d 100644
--- a/docs/manpages/0c.md
+++ b/docs/manpages/oc.md
@@ -1,21 +1,21 @@
-% 0C(1)
+% OC(1)
 % olang mantainers
 % Feb 2024
 
 # NAME
 
-0c - zero language compiler
+oc - O Programming Language compiler
 
 # SYNOPSIS
 
-**0c**
+**oc**
     source_file
     [**----dump-tokens**]
     [**--o** output_file [**----save-temps**]] 
 
 # DESCRIPTION
 
-**0c** is the offical compiler for zero language, it is also a tool that
+**oc** is the offical O programming language compiler, it is also a tool that
 contains utilities to help the language development.
 
 # GENERAL OPTIONS
diff --git a/docs/pages/hacking.md b/docs/pages/hacking.md
index ef88791..86db9d6 100644
--- a/docs/pages/hacking.md
+++ b/docs/pages/hacking.md
@@ -53,7 +53,7 @@ Testing
 -------
 
 There are two layers of tests **integration** and **unit**. The
-integration test is going to execute the **0c** compiler and check if
+integration test is going to execute the **oc** compiler and check if
 the generated binary acts as expected. Unit tests will test C functions.
 
 For both unit and integration we use **munit** framework:
diff --git a/docs/template.html b/docs/template.html
index ecc92a2..4e066d1 100644
--- a/docs/template.html
+++ b/docs/template.html
@@ -52,7 +52,7 @@
 </head>
 <body>
   <header>
-    <h1>∅lang | The zero programming language</h1>
+    <h1>olang | The O Programming Language</h1>
     <nav>
       <a href="/">Index</a> |
       <a href="/pages/getting-started.html">Getting started (WIP)</a> |
diff --git a/examples/main_exit.0 b/examples/main_exit.ol
similarity index 100%
rename from examples/main_exit.0
rename to examples/main_exit.ol
diff --git a/tests/integration/cli_runner.c b/tests/integration/cli_runner.c
index fed12ab..8541a66 100644
--- a/tests/integration/cli_runner.c
+++ b/tests/integration/cli_runner.c
@@ -24,7 +24,7 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
-#define OLANG_COMPILER_PATH "../../0c"
+#define OLANG_COMPILER_PATH "../../oc"
 
 static int compiler_exists_already_checked = 0;
 
@@ -83,7 +83,7 @@ cli_runner_compiler_dump_tokens(char *src)
 {
     cli_result_t result = { 0 };
 
-    char *program_args[] = { "0c", "--dump-tokens", src, NULL };
+    char *program_args[] = { "oc", "--dump-tokens", src, NULL };
     cli_runner_compiler(&result, program_args);
     return result;
 }
@@ -94,7 +94,7 @@ cli_runner_compiler_compile(char *src)
     cli_result_t result = { 0 };
     create_tmp_file_name(result.binary_path);
 
-    char *program_args[] = { "0c", src, "-o", result.binary_path, NULL };
+    char *program_args[] = { "oc", src, "-o", result.binary_path, NULL };
     cli_runner_compiler(&result, program_args);
     return result;
 }
diff --git a/tests/integration/cli_test.c b/tests/integration/cli_test.c
index c5896df..8cc22f9 100644
--- a/tests/integration/cli_test.c
+++ b/tests/integration/cli_test.c
@@ -22,30 +22,30 @@
 static MunitResult
 test_cli_dump_tokens(const MunitParameter params[], void *user_data_or_fixture)
 {
-    cli_result_t compilation_result = cli_runner_compiler_dump_tokens("../../examples/main_exit.0");
+    cli_result_t compilation_result = cli_runner_compiler_dump_tokens("../../examples/main_exit.ol");
     munit_assert_int(compilation_result.exec.exit_code, ==, 0);
     munit_assert_string_equal(compilation_result.exec.stdout_buf,
-                              "../../examples/main_exit.0:1:1: <fn>\n"
-                              "../../examples/main_exit.0:1:4: <identifier>\n"
-                              "../../examples/main_exit.0:1:8: <(>\n"
-                              "../../examples/main_exit.0:1:9: <)>\n"
-                              "../../examples/main_exit.0:1:10: <:>\n"
-                              "../../examples/main_exit.0:1:12: <identifier>\n"
-                              "../../examples/main_exit.0:1:16: <{>\n"
-                              "../../examples/main_exit.0:1:17: <line_feed>\n"
-                              "../../examples/main_exit.0:2:3: <return>\n"
-                              "../../examples/main_exit.0:2:10: <number>\n"
-                              "../../examples/main_exit.0:2:11: <line_feed>\n"
-                              "../../examples/main_exit.0:3:1: <}>\n"
-                              "../../examples/main_exit.0:3:2: <line_feed>\n"
-                              "../../examples/main_exit.0:4:1: <EOF>\n");
+                              "../../examples/main_exit.ol:1:1: <fn>\n"
+                              "../../examples/main_exit.ol:1:4: <identifier>\n"
+                              "../../examples/main_exit.ol:1:8: <(>\n"
+                              "../../examples/main_exit.ol:1:9: <)>\n"
+                              "../../examples/main_exit.ol:1:10: <:>\n"
+                              "../../examples/main_exit.ol:1:12: <identifier>\n"
+                              "../../examples/main_exit.ol:1:16: <{>\n"
+                              "../../examples/main_exit.ol:1:17: <line_feed>\n"
+                              "../../examples/main_exit.ol:2:3: <return>\n"
+                              "../../examples/main_exit.ol:2:10: <number>\n"
+                              "../../examples/main_exit.ol:2:11: <line_feed>\n"
+                              "../../examples/main_exit.ol:3:1: <}>\n"
+                              "../../examples/main_exit.ol:3:2: <line_feed>\n"
+                              "../../examples/main_exit.ol:4:1: <EOF>\n");
     return MUNIT_OK;
 }
 
 static MunitResult
 test_cli_compile_minimal_program(const MunitParameter params[], void *user_data_or_fixture)
 {
-    cli_result_t compilation_result = cli_runner_compiler_compile("../../examples/main_exit.0");
+    cli_result_t compilation_result = cli_runner_compiler_compile("../../examples/main_exit.ol");
     munit_assert_int(compilation_result.exec.exit_code, ==, 0);
 
     char *command_args[] = { compilation_result.binary_path, NULL };

base-commit: 3f7c8b0065817ea8505d9bf9e7721ada5d53d740
-- 
2.44.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang] site: change dns to o-lang.org
@ 2024-03-11  8:48 Johnny Richard
  2024-03-11  7:50 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-03-11  8:48 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
This change has been applied already.

To git.sr.ht:~johnnyrichard/olang
 + f602f92...3f7c8b0 HEAD -> main

 .build.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.build.yml b/.build.yml
index 18edf6c..72f0254 100644
--- a/.build.yml
+++ b/.build.yml
@@ -7,7 +7,7 @@ packages:
   - clang
   - pandoc-cli
 environment:
-  site: olang.johnnyrichard.com
+  site: o-lang.org
 sources:
   - https://git.sr.ht/~johnnyrichard/olang
 tasks:
-- 
2.44.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [RFC PATCH olang v1] docs: create zero programming language specification
@ 2024-03-09  0:05 Johnny Richard
  2024-03-08 23:09 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-03-09  0:05 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

This document specifies the semantics and behavior of the Zero Programming
Language for compiler programmers be informed how the language is designed.

This document will help newcomers to understand how the language looks
like and as a DRAFT guide on the language design discussions.

The grammar was made by using a EBNF evaluator tool[1].

[1]: https://mdkrajnak.github.io/ebnftest/

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
This grammar adds the token SEMICOLON (';') for every statement.  I know we
agreed make it optional, but the SEMICOLON makes the parser much more
convenient to implement.

And this is the first topic I would like to discuss. Let me know if you
agree otherwise I can adapt the grammar to make SEMICOLON optional.

 docs/pages/language_specification.md | 41 ++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)
 create mode 100644 docs/pages/language_specification.md

diff --git a/docs/pages/language_specification.md b/docs/pages/language_specification.md
new file mode 100644
index 0000000..9d27eda
--- /dev/null
+++ b/docs/pages/language_specification.md
@@ -0,0 +1,41 @@
+zero programming language specification
+=======================================
+
+ABSTRACT
+--------
+
+This document specifies the semantics and behavior of the Zero Programming
+Language for compiler programmers be informed how the language is designed.
+
+This specification is on DRAFT and will evolve through discussions on olang-dev
+mailing list.
+
+Language Syntax
+---------------
+
+This is the Zero Programming Language EBNF grammar specification 
+
+NOTE: This grammar spec is a DRAFT and it covers only a small portion of the
+language.
+
+```
+<program>               ::= <function-definition>
+<function-definition>   ::= <fn_keyword> <space>+ <identifier> <space>* <f-args> <space>* <colon> <space>* <type> <space>* <block>
+<identifier>            ::= <alpha>+
+                          | <alpha>+ <number>*
+                          ;
+<f-args>                ::= '(' <space>* ')'
+<block>                 ::= <ocurly> <space>* <statement>* <space>* <ccurly>
+<statement>             ::= <return-statement>
+<return-statement>      ::= <return_keyword> <space>* <number>* <space>* <semicolon>
+<semicolon>             ::= ';'
+<ocurly>                ::= '{'
+<ccurly>                ::= '}'
+<type>                  ::= 'u32 '
+<colon>                 ::= ':'
+<alpha>                 ::= #'[a-zA-Z_]'
+<number>                ::= #'[0-9]'
+<fn_keyword>            ::= 'fn'
+<return_keyword>        ::= 'return'
+<space>                 ::= #'[ \t\r\n]'
+```
-- 
2.44.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v1] ast: add ast_node root for the entire program
@ 2024-03-08 23:13 Johnny Richard
  2024-03-08 22:13 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-03-08 23:13 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Feels weird the codegen parse a function definition.  We know that in
the future we are going to parse a root node which will contains all
variables and function definition. This patch is the initial change
towards this goal.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/ast.c                  | 14 ++++++++++++++
 src/ast.h                  | 10 ++++++++++
 src/codegen_linux_x86_64.c |  8 +++++---
 src/main.c                 |  2 +-
 src/parser.c               | 12 ++++++++++++
 src/parser.h               |  2 +-
 tests/unit/parser_test.c   | 26 ++++++++++++++------------
 7 files changed, 57 insertions(+), 17 deletions(-)

diff --git a/src/ast.c b/src/ast.c
index fb39295..ab56c96 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -22,6 +22,20 @@
 #include "ast.h"
 #include "string_view.h"
 
+ast_node_t *
+ast_new_program(arena_t *arena, ast_node_t *fn_def)
+{
+    ast_node_t *node = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
+    assert(node);
+
+    node->kind = AST_NODE_PROGRAM;
+    ast_program_t *program = &node->data.as_program;
+
+    program->fn = fn_def;
+
+    return node;
+}
+
 ast_node_t *
 ast_new_node_fn_def(arena_t *arena, string_view_t identifier, type_t return_type, ast_node_t *block)
 {
diff --git a/src/ast.h b/src/ast.h
index b243334..2b42781 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -27,6 +27,7 @@ typedef struct ast_node ast_node_t;
 
 typedef enum
 {
+    AST_NODE_PROGRAM,
     AST_NODE_BLOCK,
     AST_NODE_FN_DEF,
     AST_NODE_RETURN_STMT,
@@ -44,6 +45,11 @@ typedef struct ast_block
     list_t *nodes;
 } ast_block_t;
 
+typedef struct ast_program
+{
+    ast_node_t *fn;
+} ast_program_t;
+
 typedef struct ast_fn_definition
 {
     string_view_t identifier;
@@ -74,6 +80,7 @@ typedef struct ast_return_stmt
 
 typedef union
 {
+    ast_program_t as_program;
     ast_fn_definition_t as_fn_def;
     ast_literal_t as_literal;
     ast_block_t as_block;
@@ -86,6 +93,9 @@ typedef struct ast_node
     ast_node_data_t data;
 } ast_node_t;
 
+ast_node_t *
+ast_new_program(arena_t *arena, ast_node_t *fn_def);
+
 ast_node_t *
 ast_new_node_fn_def(arena_t *arena, string_view_t identifier, type_t return_type, ast_node_t *block);
 
diff --git a/src/codegen_linux_x86_64.c b/src/codegen_linux_x86_64.c
index 3d4b17e..d4f8222 100644
--- a/src/codegen_linux_x86_64.c
+++ b/src/codegen_linux_x86_64.c
@@ -30,12 +30,14 @@ static void
 codegen_linux_x86_64_emit_function(FILE *out, ast_fn_definition_t *fn);
 
 void
-codegen_linux_x86_64_emit_program(FILE *out, ast_node_t *prog)
+codegen_linux_x86_64_emit_program(FILE *out, ast_node_t *node)
 {
     codegen_linux_x86_64_emit_start_entrypoint(out);
 
-    assert(prog->kind == AST_NODE_FN_DEF);
-    ast_fn_definition_t fn = prog->data.as_fn_def;
+    assert(node->kind == AST_NODE_PROGRAM);
+    ast_program_t program = node->data.as_program;
+
+    ast_fn_definition_t fn = program.fn->data.as_fn_def;
 
     assert(string_view_eq_to_cstr(fn.identifier, "main"));
     codegen_linux_x86_64_emit_function(out, &fn);
diff --git a/src/main.c b/src/main.c
index 785ad6d..f6d49f0 100644
--- a/src/main.c
+++ b/src/main.c
@@ -158,7 +158,7 @@ handle_codegen_linux_x86_64(cli_opts_t *opts)
     lexer_init(&lexer, file_content);
     parser_init(&parser, &lexer, &arena, opts->file_path);
 
-    ast_node_t *ast = parser_parse_fn_definition(&parser);
+    ast_node_t *ast = parser_parse_program(&parser);
 
     char asm_file[opts->output_bin.size + 3];
     sprintf(asm_file, "" SV_FMT ".s", SV_ARG(opts->output_bin));
diff --git a/src/parser.c b/src/parser.c
index a9699be..70ee8e8 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -20,6 +20,7 @@
 #include <stdio.h>
 #include <string.h>
 
+#include "ast.h"
 #include "lexer.h"
 #include "parser.h"
 
@@ -35,6 +36,9 @@ parser_parse_type(parser_t *parser, type_t *type);
 static ast_node_t *
 parser_parse_block(parser_t *parser);
 
+ast_node_t *
+parser_parse_fn_definition(parser_t *parser);
+
 static void
 skip_line_feeds(lexer_t *lexer);
 
@@ -49,6 +53,14 @@ parser_init(parser_t *parser, lexer_t *lexer, arena_t *arena, char *file_path)
     parser->file_path = file_path;
 }
 
+ast_node_t *
+parser_parse_program(parser_t *parser)
+{
+    ast_node_t *fn = parser_parse_fn_definition(parser);
+
+    return ast_new_program(parser->arena, fn);
+}
+
 ast_node_t *
 parser_parse_fn_definition(parser_t *parser)
 {
diff --git a/src/parser.h b/src/parser.h
index 3f1a00b..5bcef1d 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -33,6 +33,6 @@ void
 parser_init(parser_t *parser, lexer_t *lexer, arena_t *arena, char *file_path);
 
 ast_node_t *
-parser_parse_fn_definition(parser_t *parser);
+parser_parse_program(parser_t *parser);
 
 #endif /* PARSER_H */
diff --git a/tests/unit/parser_test.c b/tests/unit/parser_test.c
index 32ebc8e..208b1bc 100644
--- a/tests/unit/parser_test.c
+++ b/tests/unit/parser_test.c
@@ -27,7 +27,7 @@
 #define ARENA_CAPACITY (1024 * 1024)
 
 static MunitResult
-parse_fn_definition_test(const MunitParameter params[], void *user_data_or_fixture)
+parse_program_test(const MunitParameter params[], void *user_data_or_fixture)
 {
     arena_t arena = arena_new(ARENA_CAPACITY);
 
@@ -41,15 +41,19 @@ parse_fn_definition_test(const MunitParameter params[], void *user_data_or_fixtu
     parser_t parser;
     parser_init(&parser, &lexer, &arena, file_path);
 
-    ast_node_t *node_fn_def = parser_parse_fn_definition(&parser);
-    assert_not_null(node_fn_def);
-    assert_uint(node_fn_def->kind, ==, AST_NODE_FN_DEF);
+    ast_node_t *program_node = parser_parse_program(&parser);
+    assert_not_null(program_node);
+    assert_uint(program_node->kind, ==, AST_NODE_PROGRAM);
 
-    ast_fn_definition_t *fn = &node_fn_def->data.as_fn_def;
-    assert_memory_equal(fn->identifier.size, fn->identifier.chars, "main");
-    assert_uint(fn->return_type, ==, TYPE_U32);
+    ast_program_t program = program_node->data.as_program;
+    assert_not_null(program.fn);
+    assert_uint(program.fn->kind, ==, AST_NODE_FN_DEF);
 
-    ast_node_t *block = fn->block;
+    ast_fn_definition_t fn = program.fn->data.as_fn_def;
+    assert_memory_equal(fn.identifier.size, fn.identifier.chars, "main");
+    assert_uint(fn.return_type, ==, TYPE_U32);
+
+    ast_node_t *block = fn.block;
     assert_not_null(block);
 
     assert_uint(block->kind, ==, AST_NODE_BLOCK);
@@ -73,10 +77,8 @@ parse_fn_definition_test(const MunitParameter params[], void *user_data_or_fixtu
     return MUNIT_OK;
 }
 
-static MunitTest tests[] = {
-    { "/parse_fn_definition", parse_fn_definition_test, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
-    { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }
-};
+static MunitTest tests[] = { { "/parse_program", parse_program_test, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
+                             { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } };
 
 static const MunitSuite suite = { "/parser", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE };
 

base-commit: 35f594370443a2b9f73d2d2ebe573b4cab472be6
-- 
2.44.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v2 3/3] tests: add tests for the minimal possible olang program
@ 2024-03-08 22:39 Carlos Maniero
  2024-03-08 22:40 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-03-08 22:39 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

This test is just to ensure the cli is well set and it is compiling a
program given the appropriated flags.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 tests/integration/cli_runner.c | 11 +++++++++++
 tests/integration/cli_test.c   | 21 +++++++++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/tests/integration/cli_runner.c b/tests/integration/cli_runner.c
index 5a40e15..fed12ab 100644
--- a/tests/integration/cli_runner.c
+++ b/tests/integration/cli_runner.c
@@ -87,3 +87,14 @@ cli_runner_compiler_dump_tokens(char *src)
     cli_runner_compiler(&result, program_args);
     return result;
 }
+
+cli_result_t
+cli_runner_compiler_compile(char *src)
+{
+    cli_result_t result = { 0 };
+    create_tmp_file_name(result.binary_path);
+
+    char *program_args[] = { "0c", src, "-o", result.binary_path, NULL };
+    cli_runner_compiler(&result, program_args);
+    return result;
+}
diff --git a/tests/integration/cli_test.c b/tests/integration/cli_test.c
index 126f612..c5896df 100644
--- a/tests/integration/cli_test.c
+++ b/tests/integration/cli_test.c
@@ -17,6 +17,7 @@
 #define MUNIT_ENABLE_ASSERT_ALIASES
 #include "cli_runner.h"
 #include "munit.h"
+#include <stdio.h>
 
 static MunitResult
 test_cli_dump_tokens(const MunitParameter params[], void *user_data_or_fixture)
@@ -41,8 +42,28 @@ test_cli_dump_tokens(const MunitParameter params[], void *user_data_or_fixture)
     return MUNIT_OK;
 }
 
+static MunitResult
+test_cli_compile_minimal_program(const MunitParameter params[], void *user_data_or_fixture)
+{
+    cli_result_t compilation_result = cli_runner_compiler_compile("../../examples/main_exit.0");
+    munit_assert_int(compilation_result.exec.exit_code, ==, 0);
+
+    char *command_args[] = { compilation_result.binary_path, NULL };
+
+    proc_exec_command_t command = { .path = command_args[0], .args = command_args };
+
+    proc_exec(&command);
+
+    remove(command_args[0]);
+
+    munit_assert_int(command.result.exit_code, ==, 0);
+
+    return MUNIT_OK;
+}
+
 static MunitTest tests[] = {
     { "/test_cli_dump_tokens", test_cli_dump_tokens, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
+    { "/test_cli_compile_minimal_program", test_cli_compile_minimal_program, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
     { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }
 };
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang 3/3] tests: add tests for the minimal possible olang program
@ 2024-03-07 23:23 Carlos Maniero
  2024-03-07 23:24 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-03-07 23:23 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

This test is just to ensure the cli is well set and it is compiling a
program given the appropriated flags.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 tests/integration/cli_runner.c | 11 +++++++++++
 tests/integration/cli_test.c   | 21 +++++++++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/tests/integration/cli_runner.c b/tests/integration/cli_runner.c
index 5a40e15..fed12ab 100644
--- a/tests/integration/cli_runner.c
+++ b/tests/integration/cli_runner.c
@@ -87,3 +87,14 @@ cli_runner_compiler_dump_tokens(char *src)
     cli_runner_compiler(&result, program_args);
     return result;
 }
+
+cli_result_t
+cli_runner_compiler_compile(char *src)
+{
+    cli_result_t result = { 0 };
+    create_tmp_file_name(result.binary_path);
+
+    char *program_args[] = { "0c", src, "-o", result.binary_path, NULL };
+    cli_runner_compiler(&result, program_args);
+    return result;
+}
diff --git a/tests/integration/cli_test.c b/tests/integration/cli_test.c
index 126f612..c5896df 100644
--- a/tests/integration/cli_test.c
+++ b/tests/integration/cli_test.c
@@ -17,6 +17,7 @@
 #define MUNIT_ENABLE_ASSERT_ALIASES
 #include "cli_runner.h"
 #include "munit.h"
+#include <stdio.h>
 
 static MunitResult
 test_cli_dump_tokens(const MunitParameter params[], void *user_data_or_fixture)
@@ -41,8 +42,28 @@ test_cli_dump_tokens(const MunitParameter params[], void *user_data_or_fixture)
     return MUNIT_OK;
 }
 
+static MunitResult
+test_cli_compile_minimal_program(const MunitParameter params[], void *user_data_or_fixture)
+{
+    cli_result_t compilation_result = cli_runner_compiler_compile("../../examples/main_exit.0");
+    munit_assert_int(compilation_result.exec.exit_code, ==, 0);
+
+    char *command_args[] = { compilation_result.binary_path, NULL };
+
+    proc_exec_command_t command = { .path = command_args[0], .args = command_args };
+
+    proc_exec(&command);
+
+    remove(command_args[0]);
+
+    munit_assert_int(command.result.exit_code, ==, 0);
+
+    return MUNIT_OK;
+}
+
 static MunitTest tests[] = {
     { "/test_cli_dump_tokens", test_cli_dump_tokens, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
+    { "/test_cli_compile_minimal_program", test_cli_compile_minimal_program, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
     { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }
 };
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v2 4/4] parser: create simplified parser for tiny AST
@ 2024-03-01 22:24 Johnny Richard
  2024-03-01 21:32 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-03-01 22:24 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

This commit introduces a simple and restricted parser designed to handle
a small program structure. Its purpose is to lay the foundation for
future optimizations.

Error handling during syntax analysis is rudimentary. If an error
occurs, it will be printed, and the program will abort without further
parsing to detect additional syntax errors.

Additionally, it's important to note that semantic analysis will be
conducted at a later stage in the compiler pipeline. As only u32 type is
currently implemented, a separate type checker will not be developed.
Consequently, the AST generated during syntax analysis can be directly
passed to the backend.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
v2: 
    - replace ast_make_node_... by ast_new_node_...

    - replace function lexer_print_token_highlight to
      lexer_get_token_line, this change also removes the IO handling
      within the lexer.

 src/ast.c                |  79 +++++++++++++++
 src/ast.h                | 101 ++++++++++++++++++++
 src/lexer.c              |  14 +++
 src/lexer.h              |   4 +
 src/parser.c             | 202 +++++++++++++++++++++++++++++++++++++++
 src/parser.h             |  38 ++++++++
 tests/unit/parser_test.c |  88 +++++++++++++++++
 7 files changed, 526 insertions(+)
 create mode 100644 src/ast.c
 create mode 100644 src/ast.h
 create mode 100644 src/parser.c
 create mode 100644 src/parser.h
 create mode 100644 tests/unit/parser_test.c

diff --git a/src/ast.c b/src/ast.c
new file mode 100644
index 0000000..fb39295
--- /dev/null
+++ b/src/ast.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+
+#include "arena.h"
+#include "ast.h"
+#include "string_view.h"
+
+ast_node_t *
+ast_new_node_fn_def(arena_t *arena, string_view_t identifier, type_t return_type, ast_node_t *block)
+{
+    ast_node_t *node_fn_def = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
+    assert(node_fn_def);
+
+    node_fn_def->kind = AST_NODE_FN_DEF;
+    ast_fn_definition_t *fn_def = &node_fn_def->data.as_fn_def;
+
+    fn_def->identifier = identifier;
+    fn_def->return_type = return_type;
+    fn_def->block = block;
+
+    return node_fn_def;
+}
+
+ast_node_t *
+ast_new_node_literal_u32(arena_t *arena, uint32_t value)
+{
+    ast_node_t *node_literal = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
+    assert(node_literal);
+
+    node_literal->kind = AST_NODE_LITERAL;
+    node_literal->data.as_literal.kind = AST_LITERAL_U32;
+    node_literal->data.as_literal.value.as_u32 = value;
+
+    return node_literal;
+}
+
+ast_node_t *
+ast_new_node_return_stmt(arena_t *arena)
+{
+    ast_node_t *node_return_stmt = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
+    assert(node_return_stmt);
+
+    node_return_stmt->kind = AST_NODE_RETURN_STMT;
+
+    return node_return_stmt;
+}
+
+ast_node_t *
+ast_new_node_block(arena_t *arena)
+{
+    ast_node_t *node_block = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
+    assert(node_block);
+
+    node_block->kind = AST_NODE_BLOCK;
+
+    node_block->data.as_block.nodes = (list_t *)arena_alloc(arena, sizeof(list_t));
+    assert(node_block->data.as_block.nodes);
+
+    list_init(node_block->data.as_block.nodes, arena);
+
+    return node_block;
+}
diff --git a/src/ast.h b/src/ast.h
new file mode 100644
index 0000000..b243334
--- /dev/null
+++ b/src/ast.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#ifndef AST_H
+#define AST_H
+
+#include <stdint.h>
+
+#include "arena.h"
+#include "list.h"
+#include "string_view.h"
+
+typedef struct ast_node ast_node_t;
+
+typedef enum
+{
+    AST_NODE_BLOCK,
+    AST_NODE_FN_DEF,
+    AST_NODE_RETURN_STMT,
+    AST_NODE_LITERAL,
+    AST_NODE_UNKNOWN
+} ast_node_kind_t;
+
+typedef enum
+{
+    TYPE_U32
+} type_t;
+
+typedef struct ast_block
+{
+    list_t *nodes;
+} ast_block_t;
+
+typedef struct ast_fn_definition
+{
+    string_view_t identifier;
+    type_t return_type;
+    ast_node_t *block;
+} ast_fn_definition_t;
+
+typedef enum
+{
+    AST_LITERAL_U32
+} ast_literal_kind_t;
+
+typedef union
+{
+    uint32_t as_u32;
+} ast_literal_value_t;
+
+typedef struct ast_literal
+{
+    ast_literal_kind_t kind;
+    ast_literal_value_t value;
+} ast_literal_t;
+
+typedef struct ast_return_stmt
+{
+    ast_node_t *data;
+} ast_return_stmt_t;
+
+typedef union
+{
+    ast_fn_definition_t as_fn_def;
+    ast_literal_t as_literal;
+    ast_block_t as_block;
+    ast_return_stmt_t as_return_stmt;
+} ast_node_data_t;
+
+typedef struct ast_node
+{
+    ast_node_kind_t kind;
+    ast_node_data_t data;
+} ast_node_t;
+
+ast_node_t *
+ast_new_node_fn_def(arena_t *arena, string_view_t identifier, type_t return_type, ast_node_t *block);
+
+ast_node_t *
+ast_new_node_literal_u32(arena_t *arena, uint32_t value);
+
+ast_node_t *
+ast_new_node_return_stmt(arena_t *arena);
+
+ast_node_t *
+ast_new_node_block(arena_t *arena);
+
+#endif /* AST_H */
diff --git a/src/lexer.c b/src/lexer.c
index c7756a6..dd6f11d 100644
--- a/src/lexer.c
+++ b/src/lexer.c
@@ -19,6 +19,7 @@
 #include <assert.h>
 #include <ctype.h>
 #include <stdbool.h>
+#include <stdio.h>
 
 void
 lexer_init(lexer_t *lexer, string_view_t source)
@@ -255,3 +256,16 @@ lexer_lookahead(lexer_t *lexer, token_t *token, size_t n)
     lexer->row = previous_row;
     lexer->bol = previous_bol;
 }
+
+string_view_t
+lexer_get_token_line(lexer_t *lexer, token_t *token)
+{
+    size_t offset = token->location.bol;
+    string_view_t line = { .chars = lexer->source.chars + offset, .size = 0 };
+
+    while ((line.size + offset) < lexer->source.size && line.chars[line.size] != '\n' && line.chars[line.size] != 0) {
+        ++line.size;
+    }
+
+    return line;
+}
diff --git a/src/lexer.h b/src/lexer.h
index 729c957..cb91d7e 100644
--- a/src/lexer.h
+++ b/src/lexer.h
@@ -19,6 +19,7 @@
 
 #include "string_view.h"
 #include <stdint.h>
+#include <stdio.h>
 
 typedef struct lexer
 {
@@ -77,4 +78,7 @@ lexer_lookahead(lexer_t *lexer, token_t *token, size_t n);
 char *
 token_kind_to_cstr(token_kind_t kind);
 
+string_view_t
+lexer_get_token_line(lexer_t *lexer, token_t *token);
+
 #endif /* LEXER_H */
diff --git a/src/parser.c b/src/parser.c
new file mode 100644
index 0000000..a9699be
--- /dev/null
+++ b/src/parser.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "lexer.h"
+#include "parser.h"
+
+static bool
+skip_expected_token(parser_t *parser, token_kind_t expected_kind);
+
+static bool
+expected_token(parser_t *parser, token_t *token, token_kind_t kind);
+
+static bool
+parser_parse_type(parser_t *parser, type_t *type);
+
+static ast_node_t *
+parser_parse_block(parser_t *parser);
+
+static void
+skip_line_feeds(lexer_t *lexer);
+
+void
+parser_init(parser_t *parser, lexer_t *lexer, arena_t *arena, char *file_path)
+{
+    assert(parser && "parser is required");
+    assert(lexer && "lexer is required");
+    assert(file_path && "file_path is required");
+    parser->lexer = lexer;
+    parser->arena = arena;
+    parser->file_path = file_path;
+}
+
+ast_node_t *
+parser_parse_fn_definition(parser_t *parser)
+{
+    if (!skip_expected_token(parser, TOKEN_FN)) {
+        return NULL;
+    }
+
+    skip_line_feeds(parser->lexer);
+
+    token_t fn_name_token;
+
+    if (!expected_token(parser, &fn_name_token, TOKEN_IDENTIFIER)) {
+        return NULL;
+    }
+
+    skip_line_feeds(parser->lexer);
+
+    if (!skip_expected_token(parser, TOKEN_OPAREN)) {
+        return NULL;
+    }
+
+    skip_line_feeds(parser->lexer);
+
+    if (!skip_expected_token(parser, TOKEN_CPAREN)) {
+        return NULL;
+    }
+
+    skip_line_feeds(parser->lexer);
+
+    if (!skip_expected_token(parser, TOKEN_COLON)) {
+        return NULL;
+    }
+
+    skip_line_feeds(parser->lexer);
+
+    type_t fn_return_type;
+    if (!parser_parse_type(parser, &fn_return_type)) {
+        return NULL;
+    }
+
+    skip_line_feeds(parser->lexer);
+
+    ast_node_t *block = parser_parse_block(parser);
+    if (block == NULL) {
+        return NULL;
+    }
+
+    return ast_new_node_fn_def(parser->arena, fn_name_token.value, fn_return_type, block);
+}
+
+static bool
+parser_parse_type(parser_t *parser, type_t *type)
+{
+    token_t token;
+
+    if (!expected_token(parser, &token, TOKEN_IDENTIFIER)) {
+        return false;
+    }
+
+    if (string_view_eq_to_cstr(token.value, "u32")) {
+        *type = TYPE_U32;
+        return true;
+    }
+
+    return false;
+}
+
+static ast_node_t *
+parser_parse_block(parser_t *parser)
+{
+    token_t number_token;
+    if (!skip_expected_token(parser, TOKEN_OCURLY)) {
+        return false;
+    }
+
+    skip_line_feeds(parser->lexer);
+
+    ast_node_t *node_block = ast_new_node_block(parser->arena);
+
+    if (!skip_expected_token(parser, TOKEN_RETURN)) {
+        return false;
+    }
+
+    ast_node_t *node_return_stmt = ast_new_node_return_stmt(parser->arena);
+    assert(node_return_stmt);
+
+    if (!expected_token(parser, &number_token, TOKEN_NUMBER)) {
+        return false;
+    }
+
+    ast_node_t *literal_node = ast_new_node_literal_u32(parser->arena, string_view_to_u32(number_token.value));
+    assert(literal_node);
+
+    node_return_stmt->data.as_return_stmt.data = literal_node;
+
+    list_append(node_block->data.as_block.nodes, node_return_stmt);
+
+    if (!skip_expected_token(parser, TOKEN_LF)) {
+        return false;
+    }
+
+    skip_line_feeds(parser->lexer);
+
+    if (!skip_expected_token(parser, TOKEN_CCURLY)) {
+        return false;
+    }
+
+    return node_block;
+}
+
+static bool
+skip_expected_token(parser_t *parser, token_kind_t expected_kind)
+{
+    token_t token;
+    return expected_token(parser, &token, expected_kind);
+}
+
+static bool
+expected_token(parser_t *parser, token_t *token, token_kind_t expected_kind)
+{
+    lexer_next_token(parser->lexer, token);
+
+    if (token->kind != expected_kind) {
+        fprintf(stderr,
+                "%s:%lu:%lu: error: got <%s> token but expect <%s>\n",
+                parser->file_path,
+                token->location.row + 1,
+                (token->location.offset - token->location.bol) + 1,
+                token_kind_to_cstr(token->kind),
+                token_kind_to_cstr(expected_kind));
+
+        string_view_t line = lexer_get_token_line(parser->lexer, token);
+        fprintf(stderr, "" SV_FMT "\n", SV_ARG(line));
+        fprintf(stderr, "%*s\n", (int)(token->location.offset - token->location.bol + 1), "^");
+
+        return false;
+    }
+    return true;
+}
+
+static void
+skip_line_feeds(lexer_t *lexer)
+{
+    token_t token;
+    lexer_peek_next(lexer, &token);
+
+    while (token.kind == TOKEN_LF) {
+        lexer_next_token(lexer, &token);
+        lexer_peek_next(lexer, &token);
+    }
+}
diff --git a/src/parser.h b/src/parser.h
new file mode 100644
index 0000000..3f1a00b
--- /dev/null
+++ b/src/parser.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#ifndef PARSER_H
+#define PARSER_H
+
+#include "arena.h"
+#include "ast.h"
+#include "lexer.h"
+
+typedef struct parser
+{
+    lexer_t *lexer;
+    arena_t *arena;
+    // TODO: we should define a better place to file_path string
+    char *file_path;
+} parser_t;
+
+void
+parser_init(parser_t *parser, lexer_t *lexer, arena_t *arena, char *file_path);
+
+ast_node_t *
+parser_parse_fn_definition(parser_t *parser);
+
+#endif /* PARSER_H */
diff --git a/tests/unit/parser_test.c b/tests/unit/parser_test.c
new file mode 100644
index 0000000..32ebc8e
--- /dev/null
+++ b/tests/unit/parser_test.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#define MUNIT_ENABLE_ASSERT_ALIASES
+
+#include "arena.h"
+#include "ast.h"
+#include "lexer.h"
+#include "list.h"
+#include "munit.h"
+#include "parser.h"
+#include "string_view.h"
+
+#define ARENA_CAPACITY (1024 * 1024)
+
+static MunitResult
+parse_fn_definition_test(const MunitParameter params[], void *user_data_or_fixture)
+{
+    arena_t arena = arena_new(ARENA_CAPACITY);
+
+    char *file_path = "main.0";
+    char *source_value = "fn main(): u32 {\n\treturn 69\n}";
+
+    lexer_t lexer;
+    string_view_t source = { .chars = source_value, .size = strlen(source_value) };
+    lexer_init(&lexer, source);
+
+    parser_t parser;
+    parser_init(&parser, &lexer, &arena, file_path);
+
+    ast_node_t *node_fn_def = parser_parse_fn_definition(&parser);
+    assert_not_null(node_fn_def);
+    assert_uint(node_fn_def->kind, ==, AST_NODE_FN_DEF);
+
+    ast_fn_definition_t *fn = &node_fn_def->data.as_fn_def;
+    assert_memory_equal(fn->identifier.size, fn->identifier.chars, "main");
+    assert_uint(fn->return_type, ==, TYPE_U32);
+
+    ast_node_t *block = fn->block;
+    assert_not_null(block);
+
+    assert_uint(block->kind, ==, AST_NODE_BLOCK);
+    assert_uint(list_size(block->data.as_block.nodes), ==, 1);
+    list_item_t *block_item = list_get(block->data.as_block.nodes, 0);
+    assert_not_null(block_item);
+    assert_not_null(block_item->value);
+
+    ast_node_t *node = (ast_node_t *)block_item->value;
+    assert_not_null(node);
+    assert_uint(node->kind, ==, AST_NODE_RETURN_STMT);
+
+    ast_node_t *number_node = node->data.as_return_stmt.data;
+    assert_not_null(number_node);
+    assert_uint(number_node->kind, ==, AST_NODE_LITERAL);
+    assert_uint(number_node->data.as_literal.kind, ==, AST_LITERAL_U32);
+    assert_uint(number_node->data.as_literal.value.as_u32, ==, 69);
+
+    arena_free(&arena);
+
+    return MUNIT_OK;
+}
+
+static MunitTest tests[] = {
+    { "/parse_fn_definition", parse_fn_definition_test, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
+    { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }
+};
+
+static const MunitSuite suite = { "/parser", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE };
+
+int
+main(int argc, char *argv[])
+{
+    return munit_suite_main(&suite, NULL, argc, argv);
+    return EXIT_SUCCESS;
+}
-- 
2.43.2


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v1 4/4] parser: create simplified parser for tiny AST
@ 2024-02-28 19:04 Johnny Richard
  2024-02-28 18:11 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-02-28 19:04 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

This commit introduces a simple and restricted parser designed to handle
a small program structure. Its purpose is to lay the foundation for
future optimizations.

Error handling during syntax analysis is rudimentary. If an error
occurs, it will be printed, and the program will abort without further
parsing to detect additional syntax errors.

Additionally, it's important to note that semantic analysis will be
conducted at a later stage in the compiler pipeline. As only u32 type is
currently implemented, a separate type checker will not be developed.
Consequently, the AST generated during syntax analysis can be directly
passed to the backend.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/ast.c                |  79 ++++++++++++++++
 src/ast.h                | 101 ++++++++++++++++++++
 src/lexer.c              |  16 ++++
 src/lexer.h              |   4 +
 src/parser.c             | 193 +++++++++++++++++++++++++++++++++++++++
 src/parser.h             |  38 ++++++++
 tests/unit/parser_test.c |  88 ++++++++++++++++++
 7 files changed, 519 insertions(+)
 create mode 100644 src/ast.c
 create mode 100644 src/ast.h
 create mode 100644 src/parser.c
 create mode 100644 src/parser.h
 create mode 100644 tests/unit/parser_test.c

diff --git a/src/ast.c b/src/ast.c
new file mode 100644
index 0000000..ad3124d
--- /dev/null
+++ b/src/ast.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <stdint.h>
+
+#include "arena.h"
+#include "ast.h"
+#include "string_view.h"
+
+ast_node_t *
+ast_make_node_fn_def(arena_t *arena, string_view_t identifier, type_t return_type, ast_node_t *block)
+{
+    ast_node_t *node_fn_def = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
+    assert(node_fn_def);
+
+    node_fn_def->kind = AST_NODE_FN_DEF;
+    ast_fn_definition_t *fn_def = &node_fn_def->data.as_fn_def;
+
+    fn_def->identifier = identifier;
+    fn_def->return_type = return_type;
+    fn_def->block = block;
+
+    return node_fn_def;
+}
+
+ast_node_t *
+ast_make_node_literal_u32(arena_t *arena, uint32_t value)
+{
+    ast_node_t *node_literal = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
+    assert(node_literal);
+
+    node_literal->kind = AST_NODE_LITERAL;
+    node_literal->data.as_literal.kind = AST_LITERAL_U32;
+    node_literal->data.as_literal.value.as_u32 = value;
+
+    return node_literal;
+}
+
+ast_node_t *
+ast_make_node_return_stmt(arena_t *arena)
+{
+    ast_node_t *node_return_stmt = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
+    assert(node_return_stmt);
+
+    node_return_stmt->kind = AST_NODE_RETURN_STMT;
+
+    return node_return_stmt;
+}
+
+ast_node_t *
+ast_make_node_block(arena_t *arena)
+{
+    ast_node_t *node_block = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
+    assert(node_block);
+
+    node_block->kind = AST_NODE_BLOCK;
+
+    node_block->data.as_block.nodes = (list_t *)arena_alloc(arena, sizeof(list_t));
+    assert(node_block->data.as_block.nodes);
+
+    list_init(node_block->data.as_block.nodes, arena);
+
+    return node_block;
+}
diff --git a/src/ast.h b/src/ast.h
new file mode 100644
index 0000000..b80c067
--- /dev/null
+++ b/src/ast.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#ifndef AST_H
+#define AST_H
+
+#include <stdint.h>
+
+#include "arena.h"
+#include "list.h"
+#include "string_view.h"
+
+typedef struct ast_node ast_node_t;
+
+typedef enum
+{
+    AST_NODE_BLOCK,
+    AST_NODE_FN_DEF,
+    AST_NODE_RETURN_STMT,
+    AST_NODE_LITERAL,
+    AST_NODE_UNKNOWN
+} ast_node_kind_t;
+
+typedef enum
+{
+    TYPE_U32
+} type_t;
+
+typedef struct ast_block
+{
+    list_t *nodes;
+} ast_block_t;
+
+typedef struct ast_fn_definition
+{
+    string_view_t identifier;
+    type_t return_type;
+    ast_node_t *block;
+} ast_fn_definition_t;
+
+typedef enum
+{
+    AST_LITERAL_U32
+} ast_literal_kind_t;
+
+typedef union
+{
+    uint32_t as_u32;
+} ast_literal_value_t;
+
+typedef struct ast_literal
+{
+    ast_literal_kind_t kind;
+    ast_literal_value_t value;
+} ast_literal_t;
+
+typedef struct ast_return_stmt
+{
+    ast_node_t *data;
+} ast_return_stmt_t;
+
+typedef union
+{
+    ast_fn_definition_t as_fn_def;
+    ast_literal_t as_literal;
+    ast_block_t as_block;
+    ast_return_stmt_t as_return_stmt;
+} ast_node_data_t;
+
+typedef struct ast_node
+{
+    ast_node_kind_t kind;
+    ast_node_data_t data;
+} ast_node_t;
+
+ast_node_t *
+ast_make_node_fn_def(arena_t *arena, string_view_t identifier, type_t return_type, ast_node_t *block);
+
+ast_node_t *
+ast_make_node_literal_u32(arena_t *arena, uint32_t value);
+
+ast_node_t *
+ast_make_node_return_stmt(arena_t *arena);
+
+ast_node_t *
+ast_make_node_block(arena_t *arena);
+
+#endif /* AST_H */
diff --git a/src/lexer.c b/src/lexer.c
index c7756a6..e9e97d4 100644
--- a/src/lexer.c
+++ b/src/lexer.c
@@ -19,6 +19,7 @@
 #include <assert.h>
 #include <ctype.h>
 #include <stdbool.h>
+#include <stdio.h>
 
 void
 lexer_init(lexer_t *lexer, string_view_t source)
@@ -255,3 +256,18 @@ lexer_lookahead(lexer_t *lexer, token_t *token, size_t n)
     lexer->row = previous_row;
     lexer->bol = previous_bol;
 }
+
+void
+lexer_print_token_highlight(lexer_t *lexer, token_t *token, FILE *stream)
+{
+    size_t offset = token->location.bol;
+    char *str = lexer->source.chars + offset;
+
+    size_t i = 0;
+    while ((i + offset) < lexer->source.size && str[i] != '\n' && str[i] != 0) {
+        ++i;
+    }
+    string_view_t line = { .chars = str, .size = i };
+    fprintf(stream, "" SV_FMT "\n", SV_ARG(line));
+    fprintf(stream, "%*s\n", (int)(token->location.offset - token->location.bol + 1), "^");
+}
diff --git a/src/lexer.h b/src/lexer.h
index 729c957..d836b91 100644
--- a/src/lexer.h
+++ b/src/lexer.h
@@ -19,6 +19,7 @@
 
 #include "string_view.h"
 #include <stdint.h>
+#include <stdio.h>
 
 typedef struct lexer
 {
@@ -77,4 +78,7 @@ lexer_lookahead(lexer_t *lexer, token_t *token, size_t n);
 char *
 token_kind_to_cstr(token_kind_t kind);
 
+void
+lexer_print_token_highlight(lexer_t *lexer, token_t *token, FILE *stream);
+
 #endif /* LEXER_H */
diff --git a/src/parser.c b/src/parser.c
new file mode 100644
index 0000000..f50b61a
--- /dev/null
+++ b/src/parser.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "lexer.h"
+#include "parser.h"
+
+static bool
+skip_expected_token(parser_t *parser, token_kind_t expected_kind);
+
+static bool
+expected_token(parser_t *parser, token_t *token, token_kind_t kind);
+
+static bool
+parser_parse_type(parser_t *parser, type_t *type);
+
+static ast_node_t *
+parser_parse_block(parser_t *parser);
+
+static void
+skip_line_feeds(lexer_t *lexer);
+
+void
+parser_init(parser_t *parser, lexer_t *lexer, arena_t *arena, char *file_path)
+{
+    assert(parser && "parser is required");
+    assert(lexer && "lexer is required");
+    assert(file_path && "file_path is required");
+    parser->lexer = lexer;
+    parser->arena = arena;
+    parser->file_path = file_path;
+}
+
+ast_node_t *
+parser_parse_fn_definition(parser_t *parser)
+{
+    if (!skip_expected_token(parser, TOKEN_FN))
+        return NULL;
+
+    skip_line_feeds(parser->lexer);
+
+    token_t fn_name_token;
+
+    if (!expected_token(parser, &fn_name_token, TOKEN_IDENTIFIER))
+        return NULL;
+
+    skip_line_feeds(parser->lexer);
+
+    if (!skip_expected_token(parser, TOKEN_OPAREN))
+        return NULL;
+
+    skip_line_feeds(parser->lexer);
+
+    if (!skip_expected_token(parser, TOKEN_CPAREN))
+        return NULL;
+
+    skip_line_feeds(parser->lexer);
+
+    if (!skip_expected_token(parser, TOKEN_COLON))
+        return NULL;
+
+    skip_line_feeds(parser->lexer);
+
+    type_t fn_return_type;
+    if (!parser_parse_type(parser, &fn_return_type)) {
+        return NULL;
+    }
+
+    skip_line_feeds(parser->lexer);
+
+    ast_node_t *block = parser_parse_block(parser);
+    if (block == NULL) {
+        return NULL;
+    }
+
+    return ast_make_node_fn_def(parser->arena, fn_name_token.value, fn_return_type, block);
+}
+
+static bool
+parser_parse_type(parser_t *parser, type_t *type)
+{
+    token_t token;
+
+    if (!expected_token(parser, &token, TOKEN_IDENTIFIER)) {
+        return false;
+    }
+
+    if (string_view_eq_to_cstr(token.value, "u32")) {
+        *type = TYPE_U32;
+        return true;
+    }
+
+    return false;
+}
+
+static ast_node_t *
+parser_parse_block(parser_t *parser)
+{
+    token_t number_token;
+    if (!skip_expected_token(parser, TOKEN_OCURLY)) {
+        return false;
+    }
+
+    skip_line_feeds(parser->lexer);
+
+    ast_node_t *node_block = ast_make_node_block(parser->arena);
+
+    if (!skip_expected_token(parser, TOKEN_RETURN)) {
+        return false;
+    }
+
+    ast_node_t *node_return_stmt = ast_make_node_return_stmt(parser->arena);
+    assert(node_return_stmt);
+
+    if (!expected_token(parser, &number_token, TOKEN_NUMBER)) {
+        return false;
+    }
+
+    ast_node_t *literal_node = ast_make_node_literal_u32(parser->arena, string_view_to_u32(number_token.value));
+    assert(literal_node);
+
+    node_return_stmt->data.as_return_stmt.data = literal_node;
+
+    list_append(node_block->data.as_block.nodes, node_return_stmt);
+
+    if (!skip_expected_token(parser, TOKEN_LF)) {
+        return false;
+    }
+
+    skip_line_feeds(parser->lexer);
+
+    if (!skip_expected_token(parser, TOKEN_CCURLY)) {
+        return false;
+    }
+
+    return node_block;
+}
+
+static bool
+skip_expected_token(parser_t *parser, token_kind_t expected_kind)
+{
+    token_t token;
+    return expected_token(parser, &token, expected_kind);
+}
+
+static bool
+expected_token(parser_t *parser, token_t *token, token_kind_t expected_kind)
+{
+    lexer_next_token(parser->lexer, token);
+
+    if (token->kind != expected_kind) {
+        fprintf(stderr,
+                "%s:%lu:%lu: error: got <%s> token but expect <%s>\n",
+                parser->file_path,
+                token->location.row + 1,
+                (token->location.offset - token->location.bol) + 1,
+                token_kind_to_cstr(token->kind),
+                token_kind_to_cstr(expected_kind));
+        lexer_print_token_highlight(parser->lexer, token, stderr);
+        return false;
+    }
+    return true;
+}
+
+static void
+skip_line_feeds(lexer_t *lexer)
+{
+    token_t token;
+    lexer_peek_next(lexer, &token);
+
+    while (token.kind == TOKEN_LF) {
+        lexer_next_token(lexer, &token);
+        lexer_peek_next(lexer, &token);
+    }
+}
diff --git a/src/parser.h b/src/parser.h
new file mode 100644
index 0000000..3f1a00b
--- /dev/null
+++ b/src/parser.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#ifndef PARSER_H
+#define PARSER_H
+
+#include "arena.h"
+#include "ast.h"
+#include "lexer.h"
+
+typedef struct parser
+{
+    lexer_t *lexer;
+    arena_t *arena;
+    // TODO: we should define a better place to file_path string
+    char *file_path;
+} parser_t;
+
+void
+parser_init(parser_t *parser, lexer_t *lexer, arena_t *arena, char *file_path);
+
+ast_node_t *
+parser_parse_fn_definition(parser_t *parser);
+
+#endif /* PARSER_H */
diff --git a/tests/unit/parser_test.c b/tests/unit/parser_test.c
new file mode 100644
index 0000000..32ebc8e
--- /dev/null
+++ b/tests/unit/parser_test.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#define MUNIT_ENABLE_ASSERT_ALIASES
+
+#include "arena.h"
+#include "ast.h"
+#include "lexer.h"
+#include "list.h"
+#include "munit.h"
+#include "parser.h"
+#include "string_view.h"
+
+#define ARENA_CAPACITY (1024 * 1024)
+
+static MunitResult
+parse_fn_definition_test(const MunitParameter params[], void *user_data_or_fixture)
+{
+    arena_t arena = arena_new(ARENA_CAPACITY);
+
+    char *file_path = "main.0";
+    char *source_value = "fn main(): u32 {\n\treturn 69\n}";
+
+    lexer_t lexer;
+    string_view_t source = { .chars = source_value, .size = strlen(source_value) };
+    lexer_init(&lexer, source);
+
+    parser_t parser;
+    parser_init(&parser, &lexer, &arena, file_path);
+
+    ast_node_t *node_fn_def = parser_parse_fn_definition(&parser);
+    assert_not_null(node_fn_def);
+    assert_uint(node_fn_def->kind, ==, AST_NODE_FN_DEF);
+
+    ast_fn_definition_t *fn = &node_fn_def->data.as_fn_def;
+    assert_memory_equal(fn->identifier.size, fn->identifier.chars, "main");
+    assert_uint(fn->return_type, ==, TYPE_U32);
+
+    ast_node_t *block = fn->block;
+    assert_not_null(block);
+
+    assert_uint(block->kind, ==, AST_NODE_BLOCK);
+    assert_uint(list_size(block->data.as_block.nodes), ==, 1);
+    list_item_t *block_item = list_get(block->data.as_block.nodes, 0);
+    assert_not_null(block_item);
+    assert_not_null(block_item->value);
+
+    ast_node_t *node = (ast_node_t *)block_item->value;
+    assert_not_null(node);
+    assert_uint(node->kind, ==, AST_NODE_RETURN_STMT);
+
+    ast_node_t *number_node = node->data.as_return_stmt.data;
+    assert_not_null(number_node);
+    assert_uint(number_node->kind, ==, AST_NODE_LITERAL);
+    assert_uint(number_node->data.as_literal.kind, ==, AST_LITERAL_U32);
+    assert_uint(number_node->data.as_literal.value.as_u32, ==, 69);
+
+    arena_free(&arena);
+
+    return MUNIT_OK;
+}
+
+static MunitTest tests[] = {
+    { "/parse_fn_definition", parse_fn_definition_test, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
+    { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }
+};
+
+static const MunitSuite suite = { "/parser", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE };
+
+int
+main(int argc, char *argv[])
+{
+    return munit_suite_main(&suite, NULL, argc, argv);
+    return EXIT_SUCCESS;
+}
-- 
2.43.2


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v3] arena: optimization: ensure alignment memory access
@ 2024-02-28 14:25 Carlos Maniero
  2024-02-28 14:26 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-02-28 14:25 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

This commit changes the pointers returned by *arena_alloc* to always be
16 bytes aligned. Non-aligned data structure could have a huge impact on
performance. Take the example bellow:

  int main() {
      void *pointer = malloc(1024);

      int offset = 0;
      long long* data = pointer + offset;

      for (int i = 0; i < INT_MAX; i++) {
          *data += i;
      }

      printf("result = %lld", *data);
  }

These are the execution times in my machine:

+----------+----------------+
| Offset   | Execution time |
+----------+----------------+
| 0 bytes  | 0m1.655s       |
| 1 bytes  | 0m2.286s       |
| 2 bytes  | 0m2.282s       |
| 4 bytes  | 0m1.716s       |
| 8 bytes  | 0m1.712s       |
| 16 bytes | 0m1.665s       |
+----------+----------------+

The reason of the performance degradation can be found at Intel's manual
[1]:

> To improve the performance of programs, data structures (especially
> stacks) should be aligned on natural boundaries whenever possible. The
> reason for this is that the processor requires two memory accesses to
> make an unaligned memory access; aligned accesses require only one
> memory access.

Double Quadwords has 16 bytes natural boundary and this is the highest
natural boundary possible on a x86_64 architecture. Also, all other
natural boundaries are power of two, meaning that any other word will
also be aligned when using 16 bytes alignment.

You can learn more about memory alignment on Drake's and Berg's
"Unaligned Memory Accesses" article [2].

[1]: Intel® 64 and IA-32 Architectures Software Developer’s Manual
     Combined Volumes: 1, 2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D, and 4"
     Chapter 4.1.1 "Alignment of Words, Doublewords, Quadwords, and
     Double Quadwords
     https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html

[2]: https://www.kernel.org/doc/html/next/_sources/core-api/unaligned-memory-access.rst.txt

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
v3:
- Include more details on commit message explaining why it metters.
- Enforces 16 bytes aligment on all platforms.
- Remove duplicated test scenario (overflow test).
 src/arena.c             | 16 +++++++++++---
 src/arena.h             |  3 +++
 tests/unit/arena_test.c | 48 +++++++++++++++++++++++++++++++++++------
 3 files changed, 58 insertions(+), 9 deletions(-)

diff --git a/src/arena.c b/src/arena.c
index ae33e6a..ad2e535 100644
--- a/src/arena.c
+++ b/src/arena.c
@@ -28,14 +28,18 @@ arena_new(size_t size)
     return arena;
 }
 
+static uint8_t
+arena_padding(size_t bytes);
+
 void *
-arena_alloc(arena_t *arena, size_t size)
+arena_alloc(arena_t *arena, size_t bytes)
 {
-    if ((arena->offset + size) > arena->size) {
+    if ((arena->offset + bytes) > arena->size) {
         return NULL;
     }
     void *pointer = arena->region + arena->offset;
-    arena->offset += size;
+    arena->offset += bytes + arena_padding(bytes);
+
     return pointer;
 }
 
@@ -51,3 +55,9 @@ arena_free(arena_t *arena)
     arena->size = 0;
     free(arena->region);
 }
+
+static uint8_t
+arena_padding(size_t bytes)
+{
+    return (ARENA_ALIGNMENT_BYTES - bytes) & ARENA_ALIGNMENT_BYTES_MASK;
+}
diff --git a/src/arena.h b/src/arena.h
index 157165c..03fd803 100644
--- a/src/arena.h
+++ b/src/arena.h
@@ -19,6 +19,9 @@
 #include <stdint.h>
 #include <stdlib.h>
 
+#define ARENA_ALIGNMENT_BYTES 16
+#define ARENA_ALIGNMENT_BYTES_MASK (ARENA_ALIGNMENT_BYTES - 1)
+
 typedef struct arena
 {
     size_t offset;
diff --git a/tests/unit/arena_test.c b/tests/unit/arena_test.c
index 13f406f..a471572 100644
--- a/tests/unit/arena_test.c
+++ b/tests/unit/arena_test.c
@@ -19,14 +19,14 @@
 #include "munit.h"
 
 static MunitResult
-arena_test(const MunitParameter params[], void *user_data_or_fixture)
+arena_alloc_test(const MunitParameter params[], void *user_data_or_fixture)
 {
-    arena_t arena = arena_new(sizeof(int) * 2);
+    arena_t arena = arena_new(ARENA_ALIGNMENT_BYTES * 2);
 
-    int *a = arena_alloc(&arena, sizeof(int));
+    uint8_t *a = arena_alloc(&arena, sizeof(uint8_t));
     *a = 1;
 
-    int *b = arena_alloc(&arena, sizeof(int));
+    uint8_t *b = arena_alloc(&arena, sizeof(uint8_t));
     *b = 2;
 
     munit_assert_int(*a, ==, 1);
@@ -34,7 +34,7 @@ arena_test(const MunitParameter params[], void *user_data_or_fixture)
 
     arena_release(&arena);
 
-    int *c = arena_alloc(&arena, sizeof(int));
+    uint8_t *c = arena_alloc(&arena, sizeof(uint8_t));
     *c = 3;
 
     munit_assert_int(*c, ==, 3);
@@ -49,7 +49,43 @@ arena_test(const MunitParameter params[], void *user_data_or_fixture)
     return MUNIT_OK;
 }
 
-static MunitTest tests[] = { { "/arena_test", arena_test, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
+static MunitResult
+arena_padding_test(const MunitParameter params[], void *user_data_or_fixture)
+{
+    arena_t arena = arena_new(512);
+
+    // Allocated bytes is < ARENA_ALIGNMENT_BYTES
+    uint8_t *a = arena_alloc(&arena, sizeof(uint8_t));
+    uint8_t *b = arena_alloc(&arena, sizeof(uint8_t));
+
+    munit_assert_int((b - a) % ARENA_ALIGNMENT_BYTES, ==, 0);
+    munit_assert_int(b - a, ==, ARENA_ALIGNMENT_BYTES);
+
+    arena_release(&arena);
+
+    // Allocated bytes is == ARENA_ALIGNMENT_BYTES
+    a = arena_alloc(&arena, ARENA_ALIGNMENT_BYTES);
+    b = arena_alloc(&arena, sizeof(uint8_t));
+
+    munit_assert_int((b - a) % ARENA_ALIGNMENT_BYTES, ==, 0);
+    munit_assert_int(b - a, ==, ARENA_ALIGNMENT_BYTES);
+
+    arena_release(&arena);
+
+    // Allocated bytes is > ARENA_ALIGNMENT_BYTES
+    a = arena_alloc(&arena, ARENA_ALIGNMENT_BYTES + 1);
+    b = arena_alloc(&arena, sizeof(uint8_t));
+
+    munit_assert_int((b - a) % ARENA_ALIGNMENT_BYTES, ==, 0);
+    munit_assert_int(b - a, ==, ARENA_ALIGNMENT_BYTES * 2);
+
+    arena_free(&arena);
+
+    return MUNIT_OK;
+}
+
+static MunitTest tests[] = { { "/arena_alloc_test", arena_alloc_test, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
+                             { "/arena_padding_test", arena_padding_test, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
                              { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } };
 
 static const MunitSuite suite = { "/arena", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE };
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v2] cli: replace memory allocation malloc -> arena
@ 2024-02-28 12:37 Johnny Richard
  2024-02-28 11:39 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-02-28 12:37 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

This code assumes that 1MB will be enough space to accommodate the
entire source code and the AST.  If the source code is greater than half
of the arena capacity, the program will crash.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
v2: add TODO comment to ajust the arena capacity in the furure

 src/main.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/src/main.c b/src/main.c
index 978b770..2b2f12a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -14,15 +14,20 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
+#include <assert.h>
 #include <errno.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
+#include "arena.h"
 #include "lexer.h"
 #include "string_view.h"
 
+// TODO: find a better solution to define the arena capacity
+#define ARENA_CAPACITY (1024 * 1024)
+
 typedef struct cli_args
 {
     int argc;
@@ -45,7 +50,7 @@ static void
 print_token(char *file_path, token_t *token);
 
 string_view_t
-read_entire_file(char *file_path);
+read_entire_file(char *file_path, arena_t *arena);
 
 int
 main(int argc, char **argv)
@@ -73,7 +78,8 @@ main(int argc, char **argv)
         return EXIT_FAILURE;
     }
 
-    string_view_t file_content = read_entire_file(opts.file_path);
+    arena_t arena = arena_new(ARENA_CAPACITY);
+    string_view_t file_content = read_entire_file(opts.file_path, &arena);
 
     lexer_t lexer = { 0 };
     lexer_init(&lexer, file_content);
@@ -107,7 +113,7 @@ print_usage(FILE *stream, char *prog)
 }
 
 string_view_t
-read_entire_file(char *file_path)
+read_entire_file(char *file_path, arena_t *arena)
 {
     FILE *stream = fopen(file_path, "rb");
 
@@ -122,7 +128,9 @@ read_entire_file(char *file_path)
     file_content.size = ftell(stream);
     fseek(stream, 0, SEEK_SET);
 
-    file_content.chars = (char *)malloc(file_content.size);
+    assert(file_content.size * 2 < ARENA_CAPACITY);
+
+    file_content.chars = (char *)arena_alloc(arena, (size_t)file_content.size);
 
     if (file_content.chars == NULL) {
         fprintf(stderr, "Could not read file %s: %s\n", file_path, strerror(errno));
-- 
2.43.2


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v2 2/2] utils: create hash map data structure
@ 2024-02-27 19:59 Johnny Richard
  2024-02-27 19:01 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-02-27 19:59 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

This is a simple hash map implementation using FNV1A 32 bits hash
function.  The map starts with capacity of 32 but is expandable.

Initially there is no function to destroy the map.  Hence, the memory
ownership will be on the callee.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/map.c             | 140 ++++++++++++++++++++++++++++++++++++++++++
 src/map.h             |  59 ++++++++++++++++++
 tests/unit/map_test.c |  77 +++++++++++++++++++++++
 3 files changed, 276 insertions(+)
 create mode 100644 src/map.c
 create mode 100644 src/map.h
 create mode 100644 tests/unit/map_test.c

diff --git a/src/map.c b/src/map.c
new file mode 100644
index 0000000..6665e18
--- /dev/null
+++ b/src/map.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#include "map.h"
+#include "arena.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static uint32_t
+u32_fnv1a_hash(const char *s);
+
+static void
+map_init(map_t *map);
+
+static char *
+_strdup(const char *s, arena_t *arena);
+
+static uint32_t
+map_get_index(map_t *map, uint32_t hash);
+
+map_t *
+map_new(arena_t *arena)
+{
+    map_t *map = (map_t *)arena_alloc(arena, sizeof(map_t));
+    if (map == NULL) {
+        fprintf(stderr, "[FATAL] Out of memory: map_new: %s\n", strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+    map->arena = arena;
+    map_init(map);
+    return map;
+}
+
+static void
+map_init(map_t *map)
+{
+    assert(map);
+    map->entries = (map_entry_t *)arena_alloc(map->arena, MAP_INITIAL_CAPACITY * sizeof(map_entry_t));
+    assert(map->entries != NULL);
+    memset(map->entries, 0, MAP_INITIAL_CAPACITY * sizeof(map_entry_t));
+    if (map->entries == NULL) {
+        fprintf(stderr, "[FATAL] Out of memory: map_init: %s\n", strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+    map->capacity = MAP_INITIAL_CAPACITY;
+}
+
+static uint32_t
+u32_fnv1a_hash(const char *s)
+{
+    uint32_t hash = U32_FNV1A_OFFSET_BASIS;
+    size_t len = strlen(s);
+    for (size_t i = 0; i < len; ++i) {
+        hash ^= s[i];
+        hash *= U32_FNV1A_PRIME;
+    }
+    return hash;
+}
+
+bool
+map_put(map_t *map, char *key, void *value)
+{
+    assert(map && key);
+    uint32_t hash = u32_fnv1a_hash(key);
+    map_entry_t *entry = map->entries + map_get_index(map, hash);
+
+    if (entry->key == NULL) {
+        *entry = (map_entry_t){ .key = _strdup(key, map->arena), .hash = hash, .value = value, .next = NULL };
+        return true;
+    }
+
+    do {
+        if (entry->hash == hash && strcmp(entry->key, key) == 0) {
+            entry->value = value;
+            break;
+        }
+        if (entry->next == NULL) {
+            entry->next = (map_entry_t *)arena_alloc(map->arena, sizeof(map_entry_t));
+            *entry->next = (map_entry_t){ .key = _strdup(key, map->arena), .hash = hash, .value = value, .next = NULL };
+
+            break;
+        }
+        entry = entry->next;
+    } while (entry != NULL);
+
+    return true;
+}
+
+void *
+map_get(map_t *map, char *key)
+{
+    uint32_t hash = u32_fnv1a_hash(key);
+    map_entry_t *entry = map->entries + map_get_index(map, hash);
+    while (entry != NULL) {
+        if (entry->hash == hash && strcmp(entry->key, key) == 0) {
+            return entry->value;
+        }
+        entry = entry->next;
+    }
+    return NULL;
+}
+
+static uint32_t
+map_get_index(map_t *map, uint32_t hash)
+{
+    uint32_t capacity_mask = map->capacity - 1;
+    return hash & capacity_mask;
+}
+
+static char *
+_strdup(const char *s, arena_t *arena)
+{
+    size_t slen = strlen(s);
+    char *result = arena_alloc(arena, slen + 1);
+    if (result == NULL) {
+        return NULL;
+    }
+
+    memcpy(result, s, slen + 1);
+    return result;
+}
diff --git a/src/map.h b/src/map.h
new file mode 100644
index 0000000..123ad4d
--- /dev/null
+++ b/src/map.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#ifndef MAP_H
+#define MAP_H
+
+#include "arena.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#define MAP_INITIAL_CAPACITY 32
+
+#define U32_FNV1A_PRIME 0x01000193
+#define U32_FNV1A_OFFSET_BASIS 0x811c9dc5
+
+typedef struct map map_t;
+typedef struct map_bucket map_bucket_t;
+typedef struct map_entry map_entry_t;
+
+typedef struct map
+{
+    arena_t *arena;
+    map_entry_t *entries;
+    size_t capacity;
+} map_t;
+
+typedef struct map_entry
+{
+    char *key;
+    void *value;
+    uint32_t hash;
+    map_entry_t *next;
+} map_entry_t;
+
+map_t *
+map_new(arena_t *arena);
+
+bool
+map_put(map_t *map, char *key, void *value);
+
+void *
+map_get(map_t *map, char *key);
+
+#endif /* MAP_H */
diff --git a/tests/unit/map_test.c b/tests/unit/map_test.c
new file mode 100644
index 0000000..449bca6
--- /dev/null
+++ b/tests/unit/map_test.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#define MUNIT_ENABLE_ASSERT_ALIASES
+
+#include "arena.h"
+#include "map.h"
+#include "munit.h"
+#include <stdio.h>
+
+#define MAP_TEST_ARENA_CAPACITY (1024 * 16)
+
+static MunitResult
+test_create_new(const MunitParameter params[], void *user_data_or_fixture)
+{
+    arena_t arena = arena_new(MAP_TEST_ARENA_CAPACITY);
+
+    map_t *map = map_new(&arena);
+
+    assert_not_null(map);
+
+    arena_free(&arena);
+
+    return MUNIT_OK;
+}
+
+static MunitResult
+test_map_put_and_get(const MunitParameter params[], void *user_data_or_fixture)
+{
+    arena_t arena = arena_new(MAP_TEST_ARENA_CAPACITY);
+    map_t *map = map_new(&arena);
+
+    int n1 = 1;
+    int n2 = 2;
+
+    map_put(map, "n1", (void *)&n1);
+    map_put(map, "n2", (void *)&n2);
+
+    assert_int(*((int *)map_get(map, "n1")), ==, n1);
+    assert_int(*((int *)map_get(map, "n2")), ==, n2);
+
+    map_put(map, "n1", (void *)&n2);
+
+    assert_int(*((int *)map_get(map, "n1")), ==, n2);
+
+    arena_free(&arena);
+
+    return MUNIT_OK;
+}
+
+static MunitTest tests[] = {
+    { "/test_create_new", test_create_new, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
+    { "/test_map_put_and_get", test_map_put_and_get, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
+    { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }
+};
+
+static const MunitSuite suite = { "/map", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE };
+
+int
+main(int argc, char *argv[])
+{
+    return munit_suite_main(&suite, NULL, argc, argv);
+    return EXIT_SUCCESS;
+}
-- 
2.43.2


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang] test: fix suite name for list_test and arena_test
@ 2024-02-24 20:40 Johnny Richard
  2024-02-24 19:42 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-02-24 20:40 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 tests/unit/arena_test.c | 2 +-
 tests/unit/list_test.c  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/tests/unit/arena_test.c b/tests/unit/arena_test.c
index 6310795..13f406f 100644
--- a/tests/unit/arena_test.c
+++ b/tests/unit/arena_test.c
@@ -52,7 +52,7 @@ arena_test(const MunitParameter params[], void *user_data_or_fixture)
 static MunitTest tests[] = { { "/arena_test", arena_test, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
                              { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } };
 
-static const MunitSuite suite = { "/cli_test", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE };
+static const MunitSuite suite = { "/arena", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE };
 
 int
 main(int argc, char *argv[])
diff --git a/tests/unit/list_test.c b/tests/unit/list_test.c
index 33d867b..8b759f9 100644
--- a/tests/unit/list_test.c
+++ b/tests/unit/list_test.c
@@ -100,7 +100,7 @@ static MunitTest tests[] = { { "/list_append_test", list_append_test, NULL, NULL
                              { "/list_next_test", list_next_test, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
                              { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } };
 
-static const MunitSuite suite = { "/cli_test", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE };
+static const MunitSuite suite = { "/list", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE };
 
 int
 main(int argc, char *argv[])
-- 
2.43.2


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang] cli: replace memory allocation malloc -> arena
@ 2024-02-22 19:09 Johnny Richard
  2024-02-22 18:11 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-02-22 19:09 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

This code assumes that 1MB will be enough space to accommodate the
entire source code and the AST.  If the source code is greater than half
of the arena capacity, the program will crash.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/0c.c | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/src/0c.c b/src/0c.c
index 978b770..f0ac974 100644
--- a/src/0c.c
+++ b/src/0c.c
@@ -14,15 +14,19 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
+#include <assert.h>
 #include <errno.h>
 #include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
+#include "arena.h"
 #include "lexer.h"
 #include "string_view.h"
 
+#define ARENA_CAPACITY (1024 * 1024)
+
 typedef struct cli_args
 {
     int argc;
@@ -45,7 +49,7 @@ static void
 print_token(char *file_path, token_t *token);
 
 string_view_t
-read_entire_file(char *file_path);
+read_entire_file(char *file_path, arena_t *arena);
 
 int
 main(int argc, char **argv)
@@ -73,7 +77,8 @@ main(int argc, char **argv)
         return EXIT_FAILURE;
     }
 
-    string_view_t file_content = read_entire_file(opts.file_path);
+    arena_t arena = arena_new(ARENA_CAPACITY);
+    string_view_t file_content = read_entire_file(opts.file_path, &arena);
 
     lexer_t lexer = { 0 };
     lexer_init(&lexer, file_content);
@@ -107,7 +112,7 @@ print_usage(FILE *stream, char *prog)
 }
 
 string_view_t
-read_entire_file(char *file_path)
+read_entire_file(char *file_path, arena_t *arena)
 {
     FILE *stream = fopen(file_path, "rb");
 
@@ -122,7 +127,9 @@ read_entire_file(char *file_path)
     file_content.size = ftell(stream);
     fseek(stream, 0, SEEK_SET);
 
-    file_content.chars = (char *)malloc(file_content.size);
+    assert(file_content.size * 2 < ARENA_CAPACITY);
+
+    file_content.chars = (char *)arena_alloc(arena, (size_t)file_content.size);
 
     if (file_content.chars == NULL) {
         fprintf(stderr, "Could not read file %s: %s\n", file_path, strerror(errno));
-- 
2.43.2


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang] docs: add DCO information on hacking page
@ 2024-02-22 18:38 Johnny Richard
  2024-02-22 17:41 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-02-22 18:38 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

All contributions must be signed off by the author. There is a link to
the Developer Certificate of Origin version 1.1.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
I'm adding a link to the DCO document.  I'm not sure if this document
will survive for a long time.  Let me know if I should attach the DCO
text to our documentation.

 docs/pages/hacking.md | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/docs/pages/hacking.md b/docs/pages/hacking.md
index 7ebd4b3..ef88791 100644
--- a/docs/pages/hacking.md
+++ b/docs/pages/hacking.md
@@ -68,7 +68,11 @@ make check
 Submitting a patch
 ------------------
 
-Before submit a patch, ensure your code follows our coding style and is
+All contributors are required to "sign-off" their commits (using git commit -s)
+to indicate that they have agreed to the [Developer Certificate of
+Origin](https://developercertificate.org/).
+
+Before submit the patch, ensure the code follows our coding style and is
 well-tested. After that, you\'re good to follow the steps bellow.
 
 ### Step 1: Commit your changes
-- 
2.43.2


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang] build: rename 0c.c file to main.c
@ 2024-02-22 18:24 Johnny Richard
  2024-02-22 17:26 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-02-22 18:24 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

In order to make the source code easy to follow by someone who are not
involved on the development, we are renaming the 0c.c file (which is the
compiler main entrypoint) to main.c, so then anyone can guess what is
the role of this file.

Given that we still have a single executable in our code base, I think
it's fine to name the file as main.c.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/{0c.c => main.c} | 0
 tests/unit/Makefile  | 2 +-
 2 files changed, 1 insertion(+), 1 deletion(-)
 rename src/{0c.c => main.c} (100%)

diff --git a/src/0c.c b/src/main.c
similarity index 100%
rename from src/0c.c
rename to src/main.c
diff --git a/tests/unit/Makefile b/tests/unit/Makefile
index 7c6a8b3..498bf98 100644
--- a/tests/unit/Makefile
+++ b/tests/unit/Makefile
@@ -1,6 +1,6 @@
 SRCS         := $(wildcard *_test.c)
 OBJS         := $(patsubst %_test.c, %_test.o, $(SRCS))
-SUBJECT_OBJS := $(filter-out ../../build/0c.o, $(wildcard ../../build/*.o))
+SUBJECT_OBJS := $(filter-out ../../build/main.o, $(wildcard ../../build/*.o))
 CFLAGS       := -I../../src -I../shared
 TESTS        := $(patsubst %_test.c, %_test, $(SRCS))
 EXEC_TESTS   := $(patsubst %_test, ./%_test, $(TESTS))
-- 
2.43.2


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang 2/2] utils: create hash map data structure
@ 2024-02-21 22:20 Johnny Richard
  2024-02-21 21:24 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-02-21 22:20 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

This is a simple hash map implementation using FNV1A 32 bits hash
function.  The map starts with capacity of 32 but is expandable.

Initially there is no function to destroy the map.  Hence, the memory
ownership will be on the callee.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/map.c             | 128 ++++++++++++++++++++++++++++++++++++++++++
 src/map.h             |  59 +++++++++++++++++++
 tests/unit/map_test.c |  73 ++++++++++++++++++++++++
 3 files changed, 260 insertions(+)
 create mode 100644 src/map.c
 create mode 100644 src/map.h
 create mode 100644 tests/unit/map_test.c

diff --git a/src/map.c b/src/map.c
new file mode 100644
index 0000000..532ba3b
--- /dev/null
+++ b/src/map.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#include "map.h"
+#include "arena.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static uint32_t
+u32_fnv1a_hash(const char *s);
+
+static void
+map_init(map_t *map);
+
+static char *
+__strdup(const char *s, arena_t *arena);
+
+map_t *
+map_new(arena_t *arena)
+{
+    map_t *map = (map_t *)arena_alloc(arena, sizeof(map_t));
+    if (map == NULL) {
+        fprintf(stderr, "[FATAL] Out of memory: map_new: %s\n", strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+    map->arena = arena;
+    map_init(map);
+    return map;
+}
+
+static void
+map_init(map_t *map)
+{
+    assert(map);
+    map->entries = (map_entry_t *)arena_alloc(map->arena, MAP_INITIAL_CAPACITY * sizeof(map_entry_t));
+    assert(map->entries != NULL);
+    memset(map->entries, 0, MAP_INITIAL_CAPACITY * sizeof(map_entry_t));
+    if (map->entries == NULL) {
+        fprintf(stderr, "[FATAL] Out of memory: map_init: %s\n", strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+    map->capacity = MAP_INITIAL_CAPACITY;
+}
+
+static uint32_t
+u32_fnv1a_hash(const char *s)
+{
+    uint32_t hash = U32_FNV1A_OFFSET_BASIS;
+    size_t len = strlen(s);
+    for (size_t i = 0; i < len; ++i) {
+        hash ^= s[i];
+        hash *= U32_FNV1A_PRIME;
+    }
+    return hash;
+}
+
+bool
+map_put(map_t *map, char *key, void *value)
+{
+    assert(map && key);
+    uint32_t hash = u32_fnv1a_hash(key);
+    map_entry_t *entry = &(map->entries[hash & (map->capacity - 1)]);
+
+    while (entry != NULL) {
+        if (entry->hash == hash && strcmp(entry->key, key) == 0) {
+            entry->value = value;
+            return true;
+        }
+        if (entry->next == NULL)
+            break;
+        entry = entry->next;
+    }
+
+    if (entry->key == NULL) {
+        *entry = (map_entry_t){ .key = __strdup(key, map->arena), .hash = hash, .value = value, .next = NULL };
+    } else {
+        entry->next = (map_entry_t *)arena_alloc(map->arena, sizeof(map_entry_t));
+        *entry->next = (map_entry_t){ .key = __strdup(key, map->arena), .hash = hash, .value = value, .next = NULL };
+    }
+
+    return true;
+}
+
+void *
+map_get(map_t *map, char *key)
+{
+    uint32_t hash = u32_fnv1a_hash(key);
+    map_entry_t *entry = &map->entries[hash & (map->capacity - 1)];
+    while (entry != NULL) {
+        if (entry->hash == hash && strcmp(entry->key, key) == 0) {
+            return entry->value;
+        }
+        entry = entry->next;
+    }
+    return NULL;
+}
+
+static char *
+__strdup(const char *s, arena_t *arena)
+{
+    size_t slen = strlen(s);
+    char *result = arena_alloc(arena, slen + 1);
+    if (result == NULL) {
+        return NULL;
+    }
+
+    memcpy(result, s, slen + 1);
+    return result;
+}
diff --git a/src/map.h b/src/map.h
new file mode 100644
index 0000000..123ad4d
--- /dev/null
+++ b/src/map.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#ifndef MAP_H
+#define MAP_H
+
+#include "arena.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#define MAP_INITIAL_CAPACITY 32
+
+#define U32_FNV1A_PRIME 0x01000193
+#define U32_FNV1A_OFFSET_BASIS 0x811c9dc5
+
+typedef struct map map_t;
+typedef struct map_bucket map_bucket_t;
+typedef struct map_entry map_entry_t;
+
+typedef struct map
+{
+    arena_t *arena;
+    map_entry_t *entries;
+    size_t capacity;
+} map_t;
+
+typedef struct map_entry
+{
+    char *key;
+    void *value;
+    uint32_t hash;
+    map_entry_t *next;
+} map_entry_t;
+
+map_t *
+map_new(arena_t *arena);
+
+bool
+map_put(map_t *map, char *key, void *value);
+
+void *
+map_get(map_t *map, char *key);
+
+#endif /* MAP_H */
diff --git a/tests/unit/map_test.c b/tests/unit/map_test.c
new file mode 100644
index 0000000..3eb9acd
--- /dev/null
+++ b/tests/unit/map_test.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#define MUNIT_ENABLE_ASSERT_ALIASES
+
+#include "arena.h"
+#include "map.h"
+#include "munit.h"
+#include <stdio.h>
+
+#define MAP_TEST_ARENA_CAPACITY (1024 * 16)
+
+static MunitResult
+test_create_new(const MunitParameter params[], void *user_data_or_fixture)
+{
+    arena_t arena = arena_new(MAP_TEST_ARENA_CAPACITY);
+
+    map_t *map = map_new(&arena);
+
+    assert_not_null(map);
+
+    arena_free(&arena);
+
+    return MUNIT_OK;
+}
+
+static MunitResult
+test_map_put_and_get(const MunitParameter params[], void *user_data_or_fixture)
+{
+    arena_t arena = arena_new(MAP_TEST_ARENA_CAPACITY);
+    map_t *map = map_new(&arena);
+
+    int n1 = 1;
+    int n2 = 2;
+
+    map_put(map, "n1", (void *)&n1);
+    map_put(map, "n2", (void *)&n2);
+
+    assert_int(*((int *)map_get(map, "n1")), ==, n1);
+    assert_int(*((int *)map_get(map, "n2")), ==, n2);
+
+    arena_free(&arena);
+
+    return MUNIT_OK;
+}
+
+static MunitTest tests[] = {
+    { "/test_create_new", test_create_new, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
+    { "/test_map_put_and_get", test_map_put_and_get, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
+    { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }
+};
+
+static const MunitSuite suite = { "/map", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE };
+
+int
+main(int argc, char *argv[])
+{
+    return munit_suite_main(&suite, NULL, argc, argv);
+    return EXIT_SUCCESS;
+}
-- 
2.43.2


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v2] arena: optimization: make arena 8 bits aligned
@ 2024-02-21 15:09 Carlos Maniero
  2024-02-21 15:09 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-02-21 15:09 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

This commit changes the pointers returned by *arena_alloc* to always be
system-word aligned.

Non-aligned data structure could have a huge impact on performance. Take
the example bellow:

  int main() {
      void *pointer = malloc(1024);

      int* data = pointer + 1;

      for (int i = 0; i < INT_MAX; i++) {
          *data += i;
      }

      printf("result = %d", *data);
  }

When data is not word-aligned, the processor is required to execute
multiples load/store operation which makes the program to take almost
twice the time it could take if the data was aligned.

These are the execution results in my machine:

  pointer + 0  ->  time  0m1.668s
  pointer + 1  ->  time  0m2.285s
  pointer + 2  ->  time  0m2.286s
  pointer + 4  ->  time  0m1.722s
  pointer + 8  ->  time  0m1.707s

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
v2: 
 - There was I bug on the padding logic. I fixed it and tests were
   added.

 src/arena.c             | 16 +++++++++---
 src/arena.h             |  3 +++
 tests/unit/arena_test.c | 56 +++++++++++++++++++++++++++++++++++------
 3 files changed, 65 insertions(+), 10 deletions(-)

diff --git a/src/arena.c b/src/arena.c
index ae33e6a..ad2e535 100644
--- a/src/arena.c
+++ b/src/arena.c
@@ -28,14 +28,18 @@ arena_new(size_t size)
     return arena;
 }
 
+static uint8_t
+arena_padding(size_t bytes);
+
 void *
-arena_alloc(arena_t *arena, size_t size)
+arena_alloc(arena_t *arena, size_t bytes)
 {
-    if ((arena->offset + size) > arena->size) {
+    if ((arena->offset + bytes) > arena->size) {
         return NULL;
     }
     void *pointer = arena->region + arena->offset;
-    arena->offset += size;
+    arena->offset += bytes + arena_padding(bytes);
+
     return pointer;
 }
 
@@ -51,3 +55,9 @@ arena_free(arena_t *arena)
     arena->size = 0;
     free(arena->region);
 }
+
+static uint8_t
+arena_padding(size_t bytes)
+{
+    return (ARENA_ALIGNMENT_BYTES - bytes) & ARENA_ALIGNMENT_BYTES_MASK;
+}
diff --git a/src/arena.h b/src/arena.h
index 157165c..37a36aa 100644
--- a/src/arena.h
+++ b/src/arena.h
@@ -19,6 +19,9 @@
 #include <stdint.h>
 #include <stdlib.h>
 
+#define ARENA_ALIGNMENT_BYTES sizeof(intptr_t)
+#define ARENA_ALIGNMENT_BYTES_MASK (ARENA_ALIGNMENT_BYTES - 1)
+
 typedef struct arena
 {
     size_t offset;
diff --git a/tests/unit/arena_test.c b/tests/unit/arena_test.c
index 6310795..b380461 100644
--- a/tests/unit/arena_test.c
+++ b/tests/unit/arena_test.c
@@ -19,14 +19,14 @@
 #include "munit.h"
 
 static MunitResult
-arena_test(const MunitParameter params[], void *user_data_or_fixture)
+arena_alloc_test(const MunitParameter params[], void *user_data_or_fixture)
 {
-    arena_t arena = arena_new(sizeof(int) * 2);
+    arena_t arena = arena_new(ARENA_ALIGNMENT_BYTES * 2);
 
-    int *a = arena_alloc(&arena, sizeof(int));
+    uint8_t *a = arena_alloc(&arena, sizeof(uint8_t));
     *a = 1;
 
-    int *b = arena_alloc(&arena, sizeof(int));
+    uint8_t *b = arena_alloc(&arena, sizeof(uint8_t));
     *b = 2;
 
     munit_assert_int(*a, ==, 1);
@@ -34,7 +34,7 @@ arena_test(const MunitParameter params[], void *user_data_or_fixture)
 
     arena_release(&arena);
 
-    int *c = arena_alloc(&arena, sizeof(int));
+    uint8_t *c = arena_alloc(&arena, sizeof(uint8_t));
     *c = 3;
 
     munit_assert_int(*c, ==, 3);
@@ -49,10 +49,52 @@ arena_test(const MunitParameter params[], void *user_data_or_fixture)
     return MUNIT_OK;
 }
 
-static MunitTest tests[] = { { "/arena_test", arena_test, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
+static MunitResult
+arena_padding_test(const MunitParameter params[], void *user_data_or_fixture)
+{
+    arena_t arena = arena_new(512);
+
+    // Allocated bytes is < ARENA_ALIGNMENT_BYTES
+    uint8_t *a = arena_alloc(&arena, sizeof(uint8_t));
+    uint8_t *b = arena_alloc(&arena, sizeof(uint8_t));
+
+    munit_assert_int((b - a) % ARENA_ALIGNMENT_BYTES, ==, 0);
+    munit_assert_int(b - a, ==, ARENA_ALIGNMENT_BYTES);
+
+    arena_release(&arena);
+
+    // Allocated bytes is == ARENA_ALIGNMENT_BYTES
+    a = arena_alloc(&arena, ARENA_ALIGNMENT_BYTES);
+    b = arena_alloc(&arena, sizeof(uint8_t));
+
+    munit_assert_int((b - a) % ARENA_ALIGNMENT_BYTES, ==, 0);
+    munit_assert_int(b - a, ==, ARENA_ALIGNMENT_BYTES);
+
+    arena_release(&arena);
+
+    // Allocated bytes is > ARENA_ALIGNMENT_BYTES
+    a = arena_alloc(&arena, ARENA_ALIGNMENT_BYTES + 1);
+    b = arena_alloc(&arena, sizeof(uint8_t));
+
+    arena_release(&arena);
+
+    // Allocated bytes is > 1 byte (overflow test)
+    a = arena_alloc(&arena, UINT8_MAX + 2);
+    b = arena_alloc(&arena, sizeof(uint8_t));
+
+    munit_assert_int((b - a) % ARENA_ALIGNMENT_BYTES, ==, 0);
+    munit_assert_int(b - a, ==, UINT8_MAX + 1 + ARENA_ALIGNMENT_BYTES);
+
+    arena_free(&arena);
+
+    return MUNIT_OK;
+}
+
+static MunitTest tests[] = { { "/arena_alloc_test", arena_alloc_test, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
+                             { "/arena_padding_test", arena_padding_test, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
                              { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } };
 
-static const MunitSuite suite = { "/cli_test", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE };
+static const MunitSuite suite = { "/arena_test", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE };
 
 int
 main(int argc, char *argv[])
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang] arena: optimization: make arena 8 bits aligned
@ 2024-02-21  5:52 Carlos Maniero
  2024-02-21  5:53 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-02-21  5:52 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

Non aligned data structure could have a huge impact on performance. Take
the example bellow:

  int main() {
      void *pointer = malloc(1024);

      int* data = pointer + 1;

      for (int i = 0; i < INT_MAX; i++) {
          *data += i;
      }

      printf("result = %d", *data);
  }

This are the execution results in my machine:

  pointer + 0
  time	0m1.668s
  pointer + 1
  time	0m2.285s
  pointer + 2
  time	0m2.286s
  pointer + 4
  time	0m1.722s
  pointer + 8
  time	0m1.707s

This commit changes the pointers to always return a 8 bit aligned
pointer.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 src/arena.c             | 2 +-
 tests/unit/arena_test.c | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/arena.c b/src/arena.c
index ae33e6a..63662e3 100644
--- a/src/arena.c
+++ b/src/arena.c
@@ -35,7 +35,7 @@ arena_alloc(arena_t *arena, size_t size)
         return NULL;
     }
     void *pointer = arena->region + arena->offset;
-    arena->offset += size;
+    arena->offset += size + (size % 8);
     return pointer;
 }
 
diff --git a/tests/unit/arena_test.c b/tests/unit/arena_test.c
index 6310795..7808b64 100644
--- a/tests/unit/arena_test.c
+++ b/tests/unit/arena_test.c
@@ -21,7 +21,7 @@
 static MunitResult
 arena_test(const MunitParameter params[], void *user_data_or_fixture)
 {
-    arena_t arena = arena_new(sizeof(int) * 2);
+    arena_t arena = arena_new(16);
 
     int *a = arena_alloc(&arena, sizeof(int));
     *a = 1;
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang] utils: add linked-list
@ 2024-02-20 23:37 Carlos Maniero
  2024-02-20 23:37 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-02-20 23:37 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

Add a append only linked list. Since there is no reason to remove an
item from the list, this is a append-only list, that can be access by
index or by walked-trough using list_next.

The next api can be used like this:

    list_item_t *item = list_head(&list);

    while(true) {
        if (item == NULL) {
            break;
        }
        printf("item %d\n", *((int*)item->value));
        item = list_next(item);
    }

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 src/list.c             |  84 +++++++++++++++++++++++++++++++
 src/list.h             |  52 +++++++++++++++++++
 tests/unit/list_test.c | 110 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 246 insertions(+)
 create mode 100644 src/list.c
 create mode 100644 src/list.h
 create mode 100644 tests/unit/list_test.c

diff --git a/src/list.c b/src/list.c
new file mode 100644
index 0000000..a3dd3fe
--- /dev/null
+++ b/src/list.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#include "list.h"
+#include <assert.h>
+
+void
+list_init(list_t *list, arena_t *arena)
+{
+    assert(list != NULL);
+    list->size = 0;
+    list->arena = arena;
+    list->head = NULL;
+}
+
+void
+list_append(list_t *list, void *value)
+{
+    assert(list != NULL);
+    list_item_t *item = arena_alloc(list->arena, sizeof(list_item_t));
+    item->value = value;
+    item->next = NULL;
+    list->size++;
+
+    if (list->size == 1) {
+        list->head = item;
+        list->tail = item;
+        return;
+    }
+
+    list->tail->next = item;
+    list->tail = item;
+}
+
+list_item_t *
+list_get(list_t *list, size_t index)
+{
+    assert(list != NULL);
+    assert(index < list->size);
+
+    list_item_t *item = list->head;
+
+    while (index != 0) {
+        item = item->next;
+
+        index--;
+    }
+
+    return item;
+}
+
+list_item_t *
+list_head(list_t *list)
+{
+    assert(list != NULL);
+    return list->head;
+}
+
+list_item_t *
+list_next(list_item_t *item)
+{
+    assert(item != NULL);
+    return item->next;
+}
+
+size_t
+list_size(list_t *list)
+{
+    assert(list != NULL);
+    return list->size;
+}
diff --git a/src/list.h b/src/list.h
new file mode 100644
index 0000000..901d27e
--- /dev/null
+++ b/src/list.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#ifndef LIST_H
+#define LIST_H
+#include "arena.h"
+
+typedef struct list_item
+{
+    void *value;
+    struct list_item *next;
+} list_item_t;
+
+typedef struct list
+{
+    size_t size;
+    arena_t *arena;
+    list_item_t *head;
+    list_item_t *tail;
+} list_t;
+
+void
+list_init(list_t *list, arena_t *arena);
+
+void
+list_append(list_t *list, void *value);
+
+list_item_t *
+list_get(list_t *list, size_t index);
+
+list_item_t *
+list_head(list_t *list);
+
+list_item_t *
+list_next(list_item_t *item);
+
+size_t
+list_size(list_t *list);
+#endif
diff --git a/tests/unit/list_test.c b/tests/unit/list_test.c
new file mode 100644
index 0000000..33d867b
--- /dev/null
+++ b/tests/unit/list_test.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#define MUNIT_ENABLE_ASSERT_ALIASES
+#include "arena.h"
+#include "list.h"
+#include "munit.h"
+#include <stdio.h>
+
+static MunitResult
+list_append_test(const MunitParameter params[], void *user_data_or_fixture)
+{
+    arena_t arena = arena_new(sizeof(list_item_t));
+
+    list_t list;
+    list_init(&list, &arena);
+
+    munit_assert_int(list_size(&list), ==, 0);
+
+    int value = 42;
+    list_append(&list, &value);
+
+    munit_assert_int(list_size(&list), ==, 1);
+
+    arena_free(&arena);
+
+    return MUNIT_OK;
+}
+
+static MunitResult
+list_get_test(const MunitParameter params[], void *user_data_or_fixture)
+{
+    arena_t arena = arena_new(sizeof(list_item_t) * 3);
+
+    list_t list;
+    list_init(&list, &arena);
+
+    int a = 1;
+    int b = 2;
+    int c = 3;
+
+    list_append(&list, &a);
+    list_append(&list, &b);
+    list_append(&list, &c);
+
+    munit_assert_ptr_equal(list_get(&list, 0)->value, (void *)&a);
+    munit_assert_ptr_equal(list_get(&list, 1)->value, (void *)&b);
+    munit_assert_ptr_equal(list_get(&list, 2)->value, (void *)&c);
+
+    arena_free(&arena);
+
+    return MUNIT_OK;
+}
+
+static MunitResult
+list_next_test(const MunitParameter params[], void *user_data_or_fixture)
+{
+    arena_t arena = arena_new(sizeof(list_item_t) * 3);
+
+    list_t list;
+    list_init(&list, &arena);
+
+    int a = 1;
+    int b = 2;
+    int c = 3;
+
+    list_append(&list, &a);
+    list_append(&list, &b);
+    list_append(&list, &c);
+
+    list_item_t *item_a = list_head(&list);
+    list_item_t *item_b = list_next(item_a);
+    list_item_t *item_c = list_next(item_b);
+
+    munit_assert_ptr_equal(item_a->value, (void *)&a);
+    munit_assert_ptr_equal(item_b->value, (void *)&b);
+    munit_assert_ptr_equal(item_c->value, (void *)&c);
+    munit_assert_ptr_null(list_next(item_c));
+
+    arena_free(&arena);
+
+    return MUNIT_OK;
+}
+
+static MunitTest tests[] = { { "/list_append_test", list_append_test, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
+                             { "/list_get_test", list_get_test, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
+                             { "/list_next_test", list_next_test, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
+                             { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } };
+
+static const MunitSuite suite = { "/cli_test", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE };
+
+int
+main(int argc, char *argv[])
+{
+    return munit_suite_main(&suite, NULL, argc, argv);
+    return EXIT_SUCCESS;
+}
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v3] utils: add arena
@ 2024-02-20 17:35 Carlos Maniero
  2024-02-20 17:41 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-02-20 17:35 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

This is a simple implementation of arena. At this point arena is not
expandable. Since the size of arena is predicable and basically used
used to store pointers, but we can make it more robust in the future.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 .gitignore              |  2 +-
 Makefile                |  8 ++++--
 src/arena.c             | 53 +++++++++++++++++++++++++++++++++++
 src/arena.h             | 41 +++++++++++++++++++++++++++
 tests/unit/Makefile     | 19 ++++++++-----
 tests/unit/arena_test.c | 62 +++++++++++++++++++++++++++++++++++++++++
 6 files changed, 175 insertions(+), 10 deletions(-)
 create mode 100644 src/arena.c
 create mode 100644 src/arena.h
 create mode 100644 tests/unit/arena_test.c

diff --git a/.gitignore b/.gitignore
index 92496d7..fc7d161 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,4 +2,4 @@
 build
 *.o
 docs/site.tar.gz
-tests/integration/*_test
+tests/*/*_test
diff --git a/Makefile b/Makefile
index 555029f..ea6ba53 100644
--- a/Makefile
+++ b/Makefile
@@ -20,11 +20,13 @@ $(BUILD_DIR):
 linter: $(SRCS) $(HEADERS)
 	clang-format --dry-run --Werror $?
 	$(MAKE) -C tests/integration/ linter
+	$(MAKE) -C tests/unit/ linter
 
 .PHONY: linter-fix
 linter-fix: $(SRCS) $(HEADERS)
 	clang-format -i $?
 	$(MAKE) -C tests/integration/ linter-fix
+	$(MAKE) -C tests/unit/ linter-fix
 
 .PHONY: integration-test
 integration-test:
@@ -33,12 +35,14 @@ integration-test:
 
 .PHONY: unit-test
 unit-test:
+	$(MAKE)
 	$(MAKE) -C tests/unit/
 
 .PHONY: check
 check:
-	$(MAKE) integration-test
-	$(MAKE) unit-test
+	$(MAKE)
+	$(MAKE) -C tests/integration/
+	$(MAKE) -C tests/unit/
 
 .PHONY: docs
 docs:
diff --git a/src/arena.c b/src/arena.c
new file mode 100644
index 0000000..ae33e6a
--- /dev/null
+++ b/src/arena.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#include "arena.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+arena_t
+arena_new(size_t size)
+{
+    arena_t arena;
+    arena.offset = 0;
+    arena.region = malloc(sizeof(uint8_t) * size);
+    arena.size = size;
+    return arena;
+}
+
+void *
+arena_alloc(arena_t *arena, size_t size)
+{
+    if ((arena->offset + size) > arena->size) {
+        return NULL;
+    }
+    void *pointer = arena->region + arena->offset;
+    arena->offset += size;
+    return pointer;
+}
+
+void
+arena_release(arena_t *arena)
+{
+    arena->offset = 0;
+}
+
+void
+arena_free(arena_t *arena)
+{
+    arena->size = 0;
+    free(arena->region);
+}
diff --git a/src/arena.h b/src/arena.h
new file mode 100644
index 0000000..157165c
--- /dev/null
+++ b/src/arena.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#ifndef ARENA_H
+#define ARENA_H
+#include <stdint.h>
+#include <stdlib.h>
+
+typedef struct arena
+{
+    size_t offset;
+    size_t size;
+    uint8_t *region;
+} arena_t;
+
+arena_t
+arena_new(size_t size);
+
+void *
+arena_alloc(arena_t *arena, size_t size);
+
+void
+arena_release(arena_t *arena);
+
+void
+arena_free(arena_t *arena);
+
+#endif
diff --git a/tests/unit/Makefile b/tests/unit/Makefile
index ab250cf..7c6a8b3 100644
--- a/tests/unit/Makefile
+++ b/tests/unit/Makefile
@@ -1,10 +1,11 @@
-SRCS        := $(wildcard *_test.c)
-OBJS        := $(patsubst %_test.c, %_test.o, $(SRCS))
-CFLAGS      := -I../../src -I../shared
-TESTS       := $(patsubst %_test.c, %_test, $(SRCS))
-EXEC_TESTS  := $(patsubst %_test, ./%_test, $(TESTS))
-MUNIT_SRC   := ../shared/munit.c
-MUNIT       := ./munit.o
+SRCS         := $(wildcard *_test.c)
+OBJS         := $(patsubst %_test.c, %_test.o, $(SRCS))
+SUBJECT_OBJS := $(filter-out ../../build/0c.o, $(wildcard ../../build/*.o))
+CFLAGS       := -I../../src -I../shared
+TESTS        := $(patsubst %_test.c, %_test, $(SRCS))
+EXEC_TESTS   := $(patsubst %_test, ./%_test, $(TESTS))
+MUNIT_SRC    := ../shared/munit.c
+MUNIT        := ./munit.o
 
 .PHONY: all
 all: $(MUNIT) $(TESTS)
@@ -15,6 +16,7 @@ all: $(MUNIT) $(TESTS)
 .PHONY: clean
 clean:
 	$(RM) *.o *_test
+	$(RM) -rfv lib
 
 .PHONY: linter
 linter: $(SRCS)
@@ -24,5 +26,8 @@ linter: $(SRCS)
 linter-fix: $(SRCS)
 	clang-format -i $?
 
+%_test: $(MUNIT) $(SUBJECT_OBJS) %_test.c
+	$(CC) $? $(CFLAGS) -o $@
+
 $(MUNIT):
 	$(CC) -c $(MUNIT_SRC) $(CFLAGS) -o $(MUNIT)
diff --git a/tests/unit/arena_test.c b/tests/unit/arena_test.c
new file mode 100644
index 0000000..6310795
--- /dev/null
+++ b/tests/unit/arena_test.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#define MUNIT_ENABLE_ASSERT_ALIASES
+#include "arena.h"
+#include "munit.h"
+
+static MunitResult
+arena_test(const MunitParameter params[], void *user_data_or_fixture)
+{
+    arena_t arena = arena_new(sizeof(int) * 2);
+
+    int *a = arena_alloc(&arena, sizeof(int));
+    *a = 1;
+
+    int *b = arena_alloc(&arena, sizeof(int));
+    *b = 2;
+
+    munit_assert_int(*a, ==, 1);
+    munit_assert_int(*b, ==, 2);
+
+    arena_release(&arena);
+
+    int *c = arena_alloc(&arena, sizeof(int));
+    *c = 3;
+
+    munit_assert_int(*c, ==, 3);
+    munit_assert_int(*a, ==, 3);
+    munit_assert_int(*b, ==, 2);
+
+    munit_assert_ptr_not_null(arena_alloc(&arena, sizeof(int)));
+    munit_assert_ptr_null(arena_alloc(&arena, 1));
+
+    arena_free(&arena);
+
+    return MUNIT_OK;
+}
+
+static MunitTest tests[] = { { "/arena_test", arena_test, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
+                             { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } };
+
+static const MunitSuite suite = { "/cli_test", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE };
+
+int
+main(int argc, char *argv[])
+{
+    return munit_suite_main(&suite, NULL, argc, argv);
+    return EXIT_SUCCESS;
+}
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v5 4/4] lexer: test: add integration tests for --dump-tokens
@ 2024-02-19 20:42 Carlos Maniero
  2024-02-19 20:48 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-02-19 20:42 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero, Johnny Richard

We want to test the 0c compiler in a black box way.  This test explores
`pipe` in order to handle input/output data, and it tests the
--dump-tokens against the examples/main_exit.0.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/0c.c                       |  1 -
 tests/integration/cli_runner.c | 47 ++++++++++++++++++++++++++++++----
 tests/integration/cli_runner.h |  1 +
 tests/integration/cli_test.c   | 15 +++++++++++
 4 files changed, 58 insertions(+), 6 deletions(-)

diff --git a/src/0c.c b/src/0c.c
index e84559d..978b770 100644
--- a/src/0c.c
+++ b/src/0c.c
@@ -75,7 +75,6 @@ main(int argc, char **argv)
 
     string_view_t file_content = read_entire_file(opts.file_path);
 
-    // TODO: missing integration test for lexer tokenizing
     lexer_t lexer = { 0 };
     lexer_init(&lexer, file_content);
 
diff --git a/tests/integration/cli_runner.c b/tests/integration/cli_runner.c
index 0531bcc..7e4fe9a 100644
--- a/tests/integration/cli_runner.c
+++ b/tests/integration/cli_runner.c
@@ -20,6 +20,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/wait.h>
 #include <unistd.h>
 
 #define OLANG_COMPILER_PATH "../../0c"
@@ -62,16 +63,52 @@ create_tmp_file_name(char *file_name)
 }
 
 cli_result_t
-cli_runner_compiler_dump_tokens(char *src)
+cli_runner_compiler(char *src, char *args[])
 {
     assert_compiler_exists();
 
-    cli_result_t result;
+    cli_result_t result = { 0 };
     create_tmp_file_name(result.program_path);
 
-    char command[1024];
-    sprintf(command, "%s %s --dump-tokens", OLANG_COMPILER_PATH, src);
+    int fd_link[2];
+
+    if (pipe(fd_link) == -1) {
+        perror("pipe error.");
+        exit(1);
+    }
+
+    pid_t pid = fork();
+
+    if (pid == -1) {
+        perror("fork error.");
+        exit(1);
+    }
+
+    if (pid == 0) {
+        dup2(fd_link[1], STDOUT_FILENO);
+        close(fd_link[0]);
+        close(fd_link[1]);
+
+        execv(OLANG_COMPILER_PATH, args);
+        perror("execl error.");
+        exit(127);
+    } else {
+        close(fd_link[1]);
+        if (read(fd_link[0], result.compiler_output, sizeof(result.compiler_output)) == -1) {
+            perror("read error.");
+            exit(1);
+        }
+        int status;
+        waitpid(pid, &status, 0);
+        result.exit_code = WEXITSTATUS(status);
+    }
 
-    result.exit_code = system(command);
     return result;
 }
+
+cli_result_t
+cli_runner_compiler_dump_tokens(char *src)
+{
+    char *program_args[] = { "0c", "--dump-tokens", src, NULL };
+    return cli_runner_compiler(src, program_args);
+}
diff --git a/tests/integration/cli_runner.h b/tests/integration/cli_runner.h
index 8f4d69a..7ce4e7b 100644
--- a/tests/integration/cli_runner.h
+++ b/tests/integration/cli_runner.h
@@ -20,6 +20,7 @@ typedef struct cli_result_t
 {
     int exit_code;
     char program_path[255];
+    char compiler_output[1024];
 } cli_result_t;
 
 cli_result_t
diff --git a/tests/integration/cli_test.c b/tests/integration/cli_test.c
index ce2ed91..cb1a7df 100644
--- a/tests/integration/cli_test.c
+++ b/tests/integration/cli_test.c
@@ -23,6 +23,21 @@ test_cli_hello_file(const MunitParameter params[], void *user_data_or_fixture)
 {
     cli_result_t compilation_result = cli_runner_compiler_dump_tokens("../../examples/main_exit.0");
     munit_assert_int(compilation_result.exit_code, ==, 0);
+    munit_assert_string_equal(compilation_result.compiler_output,
+                              "../../examples/main_exit.0:1:1: <fn>\n"
+                              "../../examples/main_exit.0:1:4: <identifier>\n"
+                              "../../examples/main_exit.0:1:8: <(>\n"
+                              "../../examples/main_exit.0:1:9: <)>\n"
+                              "../../examples/main_exit.0:1:10: <:>\n"
+                              "../../examples/main_exit.0:1:12: <identifier>\n"
+                              "../../examples/main_exit.0:1:16: <{>\n"
+                              "../../examples/main_exit.0:1:17: <line_feed>\n"
+                              "../../examples/main_exit.0:2:3: <return>\n"
+                              "../../examples/main_exit.0:2:10: <number>\n"
+                              "../../examples/main_exit.0:2:11: <line_feed>\n"
+                              "../../examples/main_exit.0:3:1: <}>\n"
+                              "../../examples/main_exit.0:3:2: <line_feed>\n"
+                              "../../examples/main_exit.0:4:1: <EOF>\n");
     return MUNIT_OK;
 }
 
-- 
2.43.2


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v3 2/2] lexer: create --dump-tokens cli command
@ 2024-02-19  1:44 Johnny Richard
  2024-02-19  0:47 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-02-19  1:44 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard


This patch introduces the dump tokens interface and create the initial
setup for lexical analysis.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
Changes:

  - V2 fix linter
  - V3 fix integration tests

 .gitignore                     |   1 +
 examples/main_exit.0           |   3 +
 src/0c.c                       | 121 +++++++++++++++++-
 src/lexer.c                    | 224 +++++++++++++++++++++++++++++++++
 src/lexer.h                    |  74 +++++++++++
 tests/integration/cli_runner.c |   4 +-
 tests/integration/cli_runner.h |   2 +-
 tests/integration/cli_test.c   |   2 +-
 8 files changed, 425 insertions(+), 6 deletions(-)
 create mode 100644 examples/main_exit.0
 create mode 100644 src/lexer.c
 create mode 100644 src/lexer.h

diff --git a/.gitignore b/.gitignore
index fe64668..92496d7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@
 build
 *.o
 docs/site.tar.gz
+tests/integration/*_test
diff --git a/examples/main_exit.0 b/examples/main_exit.0
new file mode 100644
index 0000000..c86fc68
--- /dev/null
+++ b/examples/main_exit.0
@@ -0,0 +1,3 @@
+fn main(): u32 {
+  return 0
+}
diff --git a/src/0c.c b/src/0c.c
index 33ac945..e5199a7 100644
--- a/src/0c.c
+++ b/src/0c.c
@@ -14,8 +14,125 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lexer.h"
+#include "string_view.h"
+
+typedef struct cli_args
+{
+    int argc;
+    char **argv;
+} cli_args_t;
+
+char *
+cli_args_shift(cli_args_t *args);
+
+typedef struct cli_opts
+{
+    // TODO: create man page instruction for --dump-tokens option
+    bool dump_tokens;
+    char *file_path;
+} cli_opts_t;
+
+void
+print_usage(FILE *stream, char *prog);
+
+string_view_t
+read_entire_file(char *file_path);
+
 int
-main(void)
+main(int argc, char **argv)
+{
+    cli_args_t args = { .argc = argc, .argv = argv };
+    cli_opts_t opts = { 0 };
+
+    char *prog = cli_args_shift(&args);
+
+    if (argc != 3) {
+        print_usage(stderr, prog);
+        return EXIT_FAILURE;
+    }
+
+    for (char *arg = cli_args_shift(&args); arg != NULL; arg = cli_args_shift(&args)) {
+        if (strcmp(arg, "--dump-tokens") == 0) {
+            opts.dump_tokens = true;
+        } else {
+            opts.file_path = arg;
+        }
+    }
+
+    if (!opts.dump_tokens) {
+        print_usage(stderr, prog);
+        return EXIT_FAILURE;
+    }
+
+    string_view_t file_content = read_entire_file(opts.file_path);
+
+    // TODO: missing integration test for lexer tokenizing
+    lexer_t lexer = { 0 };
+    lexer_init(&lexer, file_content);
+
+    token_t token = { 0 };
+    lexer_next_token(&lexer, &token);
+    while (token.kind != TOKEN_EOF) {
+        printf("%s:%lu:%lu: <%s>\n",
+               opts.file_path,
+               token.location.row + 1,
+               (token.location.offset - token.location.bol) + 1,
+               token_kind_to_cstr(token.kind));
+        lexer_next_token(&lexer, &token);
+    }
+
+    free(file_content.chars);
+
+    return EXIT_SUCCESS;
+}
+
+char *
+cli_args_shift(cli_args_t *args)
+{
+    if (args->argc == 0)
+        return NULL;
+    --(args->argc);
+    return *(args->argv)++;
+}
+
+void
+print_usage(FILE *stream, char *prog)
+{
+    fprintf(stream, "usage: %s <source.0> --dump-tokens\n", prog);
+}
+
+string_view_t
+read_entire_file(char *file_path)
 {
-    return 0;
+    FILE *stream = fopen(file_path, "rb");
+
+    if (stream == NULL) {
+        fprintf(stderr, "Could not open file %s: %s\n", file_path, strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+
+    string_view_t file_content = { 0 };
+
+    fseek(stream, 0, SEEK_END);
+    file_content.size = ftell(stream);
+    fseek(stream, 0, SEEK_SET);
+
+    file_content.chars = (char *)malloc(file_content.size);
+
+    if (file_content.chars == NULL) {
+        fprintf(stderr, "Could not read file %s: %s\n", file_path, strerror(errno));
+        exit(EXIT_FAILURE);
+    }
+
+    fread(file_content.chars, 1, file_content.size, stream);
+    fclose(stream);
+
+    return file_content;
 }
diff --git a/src/lexer.c b/src/lexer.c
new file mode 100644
index 0000000..544a54d
--- /dev/null
+++ b/src/lexer.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#include "lexer.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdbool.h>
+
+void
+lexer_init(lexer_t *lexer, string_view_t source)
+{
+    assert(lexer);
+    lexer->source = source;
+    lexer->offset = 0;
+    lexer->row = 0;
+    lexer->bol = 0;
+}
+
+static char
+lexer_next_char(lexer_t *lexer);
+
+static void
+lexer_skip_char(lexer_t *lexer);
+
+static bool
+lexer_is_eof(lexer_t *lexer);
+
+static bool
+lexer_is_not_eof(lexer_t *lexer);
+
+static bool
+_isspace(char c);
+
+static void
+lexer_init_char_token(lexer_t *lexer, token_t *token, token_kind_t kind);
+
+static void
+lexer_init_str_token(lexer_t *lexer, token_t *token, token_kind_t kind, size_t start_offset);
+
+static token_kind_t
+lexer_str_to_token_kind(string_view_t text);
+
+void
+lexer_next_token(lexer_t *lexer, token_t *token)
+{
+    if (lexer_is_eof(lexer)) {
+        *token = (token_t){ .kind = TOKEN_EOF };
+        return;
+    }
+
+    char current_char = lexer_next_char(lexer);
+
+    if (_isspace(current_char)) {
+        while (_isspace(current_char) && lexer_is_not_eof(lexer)) {
+            lexer_skip_char(lexer);
+            current_char = lexer_next_char(lexer);
+        }
+    }
+
+    while (lexer_is_not_eof(lexer)) {
+        if (isalpha(current_char)) {
+            size_t start_offset = lexer->offset;
+            while (isalnum(current_char) && lexer_is_not_eof(lexer)) {
+                lexer_skip_char(lexer);
+                current_char = lexer_next_char(lexer);
+            }
+
+            string_view_t text = { .chars = lexer->source.chars + start_offset, .size = lexer->offset - start_offset };
+
+            lexer_init_str_token(lexer, token, lexer_str_to_token_kind(text), start_offset);
+            return;
+        }
+
+        if (isdigit(current_char)) {
+            size_t start_offset = lexer->offset;
+            while (isdigit(current_char) && lexer_is_not_eof(lexer)) {
+                lexer_skip_char(lexer);
+                current_char = lexer_next_char(lexer);
+            }
+
+            lexer_init_str_token(lexer, token, TOKEN_NUMBER, start_offset);
+            return;
+        }
+
+        switch (current_char) {
+            case '(': {
+                lexer_init_char_token(lexer, token, TOKEN_OPAREN);
+                lexer_skip_char(lexer);
+                return;
+            }
+            case ')': {
+                lexer_init_char_token(lexer, token, TOKEN_CPAREN);
+                lexer_skip_char(lexer);
+                return;
+            }
+            case ':': {
+                lexer_init_char_token(lexer, token, TOKEN_COLON);
+                lexer_skip_char(lexer);
+                return;
+            }
+            case '{': {
+                lexer_init_char_token(lexer, token, TOKEN_OCURLY);
+                lexer_skip_char(lexer);
+                return;
+            }
+            case '}': {
+                lexer_init_char_token(lexer, token, TOKEN_CCURLY);
+                lexer_skip_char(lexer);
+                return;
+            }
+            case '\n': {
+                lexer_init_char_token(lexer, token, TOKEN_LF);
+                lexer_skip_char(lexer);
+                return;
+            }
+            default: {
+                lexer_init_char_token(lexer, token, TOKEN_UNKNOWN);
+                lexer_skip_char(lexer);
+                return;
+            }
+        }
+    }
+
+    if (lexer_is_eof(lexer)) {
+        *token = (token_t){ .kind = TOKEN_EOF };
+        return;
+    }
+}
+
+static char *token_kind_str_table[] = {
+    [TOKEN_UNKNOWN] = "unknown", [TOKEN_IDENTIFIER] = "identifier",
+    [TOKEN_NUMBER] = "number",   [TOKEN_FN] = "fn",
+    [TOKEN_RETURN] = "return",   [TOKEN_LF] = "line_feed",
+    [TOKEN_OPAREN] = "(",        [TOKEN_CPAREN] = ")",
+    [TOKEN_COLON] = ":",         [TOKEN_OCURLY] = "{",
+    [TOKEN_CCURLY] = "}",        [TOKEN_EOF] = "EOF",
+};
+
+char *
+token_kind_to_cstr(token_kind_t kind)
+{
+    assert(kind < sizeof(token_kind_str_table));
+    return token_kind_str_table[kind];
+}
+
+static char
+lexer_next_char(lexer_t *lexer)
+{
+    return lexer->source.chars[lexer->offset];
+}
+
+static void
+lexer_skip_char(lexer_t *lexer)
+{
+    assert(lexer->offset < lexer->source.size);
+    if (lexer->source.chars[lexer->offset] == '\n') {
+        lexer->row++;
+        lexer->bol = ++lexer->offset;
+    } else {
+        lexer->offset++;
+    }
+}
+
+static bool
+lexer_is_eof(lexer_t *lexer)
+{
+    return lexer->offset >= lexer->source.size;
+}
+
+static bool
+lexer_is_not_eof(lexer_t *lexer)
+{
+    return !lexer_is_eof(lexer);
+}
+
+static bool
+_isspace(char c)
+{
+    return c == ' ' || c == '\f' || c == '\r' || c == '\t' || c == '\v';
+}
+
+static void
+lexer_init_char_token(lexer_t *lexer, token_t *token, token_kind_t kind)
+{
+    string_view_t str = { .chars = lexer->source.chars + lexer->offset, .size = 1 };
+    token_loc_t location = { .offset = lexer->offset, .row = lexer->row, .bol = lexer->bol };
+    *token = (token_t){ .kind = kind, .value = str, .location = location };
+}
+
+static void
+lexer_init_str_token(lexer_t *lexer, token_t *token, token_kind_t kind, size_t start_offset)
+{
+    string_view_t str = { .chars = lexer->source.chars + start_offset, .size = lexer->offset - start_offset };
+    token_loc_t location = { .offset = start_offset, .row = lexer->row, .bol = lexer->bol };
+    *token = (token_t){ .kind = kind, .value = str, .location = location };
+}
+
+static token_kind_t
+lexer_str_to_token_kind(string_view_t text)
+{
+    if (string_view_eq_to_cstr(text, "return")) {
+        return TOKEN_RETURN;
+    }
+
+    if (string_view_eq_to_cstr(text, "fn")) {
+        return TOKEN_FN;
+    }
+
+    return TOKEN_IDENTIFIER;
+}
diff --git a/src/lexer.h b/src/lexer.h
new file mode 100644
index 0000000..8c09e02
--- /dev/null
+++ b/src/lexer.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#ifndef LEXER_H
+#define LEXER_H
+
+#include "string_view.h"
+#include <stdint.h>
+
+typedef struct lexer
+{
+    string_view_t source;
+    size_t offset;
+    size_t row;
+    size_t bol;
+} lexer_t;
+
+typedef enum token_kind
+{
+    TOKEN_UNKNOWN,
+    TOKEN_IDENTIFIER,
+    TOKEN_NUMBER,
+
+    // Keywords
+    TOKEN_FN,
+    TOKEN_RETURN,
+
+    // Single char
+    TOKEN_LF,
+    TOKEN_OPAREN,
+    TOKEN_CPAREN,
+    TOKEN_COLON,
+    TOKEN_OCURLY,
+    TOKEN_CCURLY,
+    TOKEN_EOF
+} token_kind_t;
+
+typedef struct token_loc
+{
+    size_t offset;
+    size_t row;
+    size_t bol;
+} token_loc_t;
+
+typedef struct token
+{
+    token_kind_t kind;
+    string_view_t value;
+    token_loc_t location;
+} token_t;
+
+void
+lexer_init(lexer_t *lexer, string_view_t source);
+
+void
+lexer_next_token(lexer_t *lexer, token_t *token);
+
+char *
+token_kind_to_cstr(token_kind_t kind);
+
+#endif /* LEXER_H */
diff --git a/tests/integration/cli_runner.c b/tests/integration/cli_runner.c
index 4e0f7c4..0531bcc 100644
--- a/tests/integration/cli_runner.c
+++ b/tests/integration/cli_runner.c
@@ -62,7 +62,7 @@ create_tmp_file_name(char *file_name)
 }
 
 cli_result_t
-cli_runner_compile_file(char *src)
+cli_runner_compiler_dump_tokens(char *src)
 {
     assert_compiler_exists();
 
@@ -70,7 +70,7 @@ cli_runner_compile_file(char *src)
     create_tmp_file_name(result.program_path);
 
     char command[1024];
-    sprintf(command, "%s -o %s %s", OLANG_COMPILER_PATH, result.program_path, src);
+    sprintf(command, "%s %s --dump-tokens", OLANG_COMPILER_PATH, src);
 
     result.exit_code = system(command);
     return result;
diff --git a/tests/integration/cli_runner.h b/tests/integration/cli_runner.h
index 5caa319..8f4d69a 100644
--- a/tests/integration/cli_runner.h
+++ b/tests/integration/cli_runner.h
@@ -23,5 +23,5 @@ typedef struct cli_result_t
 } cli_result_t;
 
 cli_result_t
-cli_runner_compile_file(char *src);
+cli_runner_compiler_dump_tokens(char *src);
 #endif
diff --git a/tests/integration/cli_test.c b/tests/integration/cli_test.c
index c7a9557..ce2ed91 100644
--- a/tests/integration/cli_test.c
+++ b/tests/integration/cli_test.c
@@ -21,7 +21,7 @@
 static MunitResult
 test_cli_hello_file(const MunitParameter params[], void *user_data_or_fixture)
 {
-    cli_result_t compilation_result = cli_runner_compile_file("../../examples/hello.olang");
+    cli_result_t compilation_result = cli_runner_compiler_dump_tokens("../../examples/main_exit.0");
     munit_assert_int(compilation_result.exit_code, ==, 0);
     return MUNIT_OK;
 }
-- 
2.43.2


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang 2/2] tests: add unit tests configuration
@ 2024-02-18  0:50 Carlos Maniero
  2024-02-18  0:55 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-02-18  0:50 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

This commit introduces a basic configuration for unit tests. However, as
there are currently no unit tests, the linter step has not been
activated. This is because clang-format does not handle zero files
gracefully.

The linter step must be enabled once the first unit test is implemented.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 Makefile            |  6 +++++-
 tests/unit/Makefile | 28 ++++++++++++++++++++++++++++
 2 files changed, 33 insertions(+), 1 deletion(-)
 create mode 100644 tests/unit/Makefile

diff --git a/Makefile b/Makefile
index b13b41b..62297ad 100644
--- a/Makefile
+++ b/Makefile
@@ -21,7 +21,6 @@ linter: $(SRCS) $(HEADERS)
 	clang-format --dry-run --Werror $?
 	$(MAKE) -C tests/integration/ linter
 
-
 .PHONY: linter-fix
 linter-fix: $(SRCS) $(HEADERS)
 	clang-format -i $?
@@ -32,9 +31,14 @@ integration-test:
 	$(MAKE)
 	$(MAKE) -C tests/integration/
 
+.PHONY: unit-test
+unit-test:
+	$(MAKE) -C tests/unit/
+
 .PHONY: check
 check:
 	$(MAKE) integration-test
+	$(MAKE) unit-test
 
 $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
 	$(CC) $(CFLAGS) -c $< -o $@
diff --git a/tests/unit/Makefile b/tests/unit/Makefile
new file mode 100644
index 0000000..ab250cf
--- /dev/null
+++ b/tests/unit/Makefile
@@ -0,0 +1,28 @@
+SRCS        := $(wildcard *_test.c)
+OBJS        := $(patsubst %_test.c, %_test.o, $(SRCS))
+CFLAGS      := -I../../src -I../shared
+TESTS       := $(patsubst %_test.c, %_test, $(SRCS))
+EXEC_TESTS  := $(patsubst %_test, ./%_test, $(TESTS))
+MUNIT_SRC   := ../shared/munit.c
+MUNIT       := ./munit.o
+
+.PHONY: all
+all: $(MUNIT) $(TESTS)
+	@for file in $(EXEC_TESTS); do \
+                ./"$$file"; \
+        done
+
+.PHONY: clean
+clean:
+	$(RM) *.o *_test
+
+.PHONY: linter
+linter: $(SRCS)
+	clang-format --dry-run --Werror $?
+
+.PHONY: linter-fix
+linter-fix: $(SRCS)
+	clang-format -i $?
+
+$(MUNIT):
+	$(CC) -c $(MUNIT_SRC) $(CFLAGS) -o $(MUNIT)
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang] docs: deploy: replace shrt.site domain by olang.johnnyrichard.com
@ 2024-02-17 21:04 Johnny Richard
  2024-02-17 20:03 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-02-17 21:04 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
The patch is already applied:

To git.sr.ht:~johnnyrichard/olang
   1c6e4e4..afcad24  main -> main

 .build.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.build.yml b/.build.yml
index 9aa3277..18edf6c 100644
--- a/.build.yml
+++ b/.build.yml
@@ -7,7 +7,7 @@ packages:
   - clang
   - pandoc-cli
 environment:
-  site: johnnyrichard.srht.site
+  site: olang.johnnyrichard.com
 sources:
   - https://git.sr.ht/~johnnyrichard/olang
 tasks:
-- 
2.43.2


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang] docs: build: fix docs publishing task
@ 2024-02-17 20:38 Johnny Richard
  2024-02-17 19:37 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-02-17 20:38 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

For a strange reason the [ -z "$VAR" ] failed during build phase, we
will try to run this task with a regular if statement instead.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
This patch is already applied

To git.sr.ht:~johnnyrichard/olang
   892b6b9..5360b26  main -> main

 .build.yml | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/.build.yml b/.build.yml
index 42c5510..9aa3277 100644
--- a/.build.yml
+++ b/.build.yml
@@ -23,5 +23,8 @@ tasks:
       make check
   - docs-publish: |
       cd olang
-      [ -z "$BUILD_REASON" ] && hut pages publish -d $site docs/site.tar.gz
+      if [ "$BUILD_REASON" = "" ]
+      then
+        hut pages publish -d $site docs/site.tar.gz
+      fi
 
-- 
2.43.2


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang] docs: add mobile version
@ 2024-02-17 20:12 Carlos Maniero
  2024-02-17 20:17 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-02-17 20:12 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 docs/template.html | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/docs/template.html b/docs/template.html
index 758b4c4..ecc92a2 100644
--- a/docs/template.html
+++ b/docs/template.html
@@ -2,6 +2,7 @@
 <html lang="en">
 <head>
   <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <meta name="date" content='$date-meta$'>
   <title>$title$ | olang</title>
   <style>
@@ -23,7 +24,7 @@
       font-family: monospace;
       font-size: 1.1rem;
       margin: 0;
-      padding: 0;
+      padding: 0 40px;
     }
     a {
       color: var(--link-color);
@@ -37,11 +38,15 @@
     }
     article {
       line-height: 1.75rem;
+      overflow: hidden;
     }
+
     pre {
       background: rgba(0,0,0, 0.75);
       color: white;
       padding: 10px;
+      max-width: 100%;
+      overflow: auto;
     }
   </style>
 </head>
@@ -51,14 +56,13 @@
     <nav>
       <a href="/">Index</a> |
       <a href="/pages/getting-started.html">Getting started (WIP)</a> |
-      <a href="/pages/hacking.html">Hacking (WIP)</a> |
+      <a href="/pages/hacking.html">Hacking</a> |
       <a href="https://sr.ht/~johnnyrichard/olang/sources" target="_blank">Sources ↗</a> |
       <a href="https://sr.ht/~johnnyrichard/olang/lists" target="_blank">Mailing list ↗</a>
     </nav>
   </header>
   <article>
     <h1>$title$</h1>
-
     $toc$
 
     $body$
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v2] docs: add HACKING documentation
@ 2024-02-17 18:40 Carlos Maniero
  2024-02-17 18:45 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-02-17 18:40 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

The purpose of this docs is to teach newcomers developers to start
contributing with the project.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
v2:
  replaces the rst with markdown once we are using pandoc now.

 docs/Makefile         |   2 +-
 docs/pages/hacking.md | 152 +++++++++++++++++++++++++++++++++++++++++-
 docs/template.html    |   3 +
 3 files changed, 154 insertions(+), 3 deletions(-)

diff --git a/docs/Makefile b/docs/Makefile
index e5b7154..059542b 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -21,4 +21,4 @@ $(BUILD_DIR):
 	@mkdir -p $@/$(PAGES_DIR)
 
 $(BUILD_DIR)/$(PAGES_DIR)/%.html: $(PAGES_DIR)/%.md
-	$(PANDOC) -s --template template.html -f markdown -t html $< > $@
+	$(PANDOC) -s --template template.html -f markdown -t html --toc $< > $@
diff --git a/docs/pages/hacking.md b/docs/pages/hacking.md
index 2bb1338..7ebd4b3 100644
--- a/docs/pages/hacking.md
+++ b/docs/pages/hacking.md
@@ -1,3 +1,151 @@
-% Hacking WIP
+% Hacking
 
-WIP
+We're thrilled to have you here! Your interest in making olang the most
+exceptional and straightforward language ever is greatly appreciated.
+
+In this document, we'll outline how you can begin contributing to olang.
+
+First and foremost, clone the project if you haven't done so already.
+
+``` {.sh}
+git clone https://git.sr.ht/~johnnyrichard/olang
+```
+
+Dependencies
+------------
+
+The olang compiler is crafted in C. To build the project, you'll require
+merely three dependencies: **make**, **gcc** (version 11 or higher), and
+**clang-format** (version 14 or higher).
+
+As an optional step, you can install **sphinx** to refresh this
+documentation.
+
+Code style
+----------
+
+Instead of delineating every element of our coding style, we have
+adopted the use of **clang-format** to enforce the olang code style.
+Please refer to the linter section below for guidance on its
+application.
+
+### Linter
+
+Checking for linter issues:
+
+``` {.sh}
+make linter
+```
+
+Most of the common code style mistakes are fixed by:
+
+``` {.sh}
+make linter-fix
+```
+
+### .editorconfig
+
+We also provide a **.editorconfig** file at project\'s root. Follow
+<https://editorconfig.org/> to learn how to make it work with your
+favorite editor.
+
+Testing
+-------
+
+There are two layers of tests **integration** and **unit**. The
+integration test is going to execute the **0c** compiler and check if
+the generated binary acts as expected. Unit tests will test C functions.
+
+For both unit and integration we use **munit** framework:
+<https://nemequ.github.io/munit/>.
+
+To execute tests you can execute:
+
+``` {.sh}
+make check
+```
+
+Submitting a patch
+------------------
+
+Before submit a patch, ensure your code follows our coding style and is
+well-tested. After that, you\'re good to follow the steps bellow.
+
+### Step 1: Commit your changes
+
+Begin by committing your modifications with a detailed and significant
+commit message. We take great pleasure in maintaining a record of our
+changes over time. Skeptical? Execute a **git log** command and admire
+the well-documented history we've created so far.
+
+But it isn\'t all about personal preference. We use a email-driven
+workflow to propose our changes, meaning the commit message you write is
+the email the olang maintainers will get. I won't go into the perks of
+the email-driven workflow here, but you can check it out at
+<https://drewdevault.com/2018/07/02/Email-driven-git.html>.
+
+#### Best practices
+
+1. Write single-purpose commits.
+2. Write a meaningful commit message.
+3. Every commit must be production ready.
+    - If the tests or the linter fail, you should not create a fix commit.
+      Instead, you should amend the commit that caused the issue and then
+      resend the patchset.
+
+### Step 2: Create your patch
+
+You can create a patch using the command:
+
+``` {.sh}
+git format-patch --cover-letter -M origin/main -o outgoing/
+```
+
+### Step 3: Write a cover letter:
+
+The command above generates a **outgoing/0000-cover-letter.patch** file.
+
+The cover letter is like a pull request description on Github. Replace
+the \*\*\* SUBJECT HERE \*\*\* with the patchset title and the
+\*\*\* BLURB HERE \*\*\* with a synopsis of your changes.
+
+If you are sending a single-commit patch you can remove the
+**\--cover-letter** argument from **git format-patch** and skip this
+step.
+
+### Step 4: Submit your patch
+
+Make sure you have configured your **git send-email**. You can learn how
+to configure it here:
+<https://git-scm.com/docs/git-send-email#_examples>
+
+Once you have everything set you just need to send the patch over our
+mailing list.
+
+``` {.sh}
+git send-email outgoing/* --to=~johnnyrichard/olang-devel@lists.sr.ht
+```
+
+### The review process
+
+Upon submission, you'll receive an automated email from our pipeline. If
+the check is successful, the olang maintainers will review your patch.
+Subsequently, you'll receive an email indicating whether your patch has
+been approved, requires changes, or has been rejected.
+
+### Submitting changes in a patchset
+
+If your patchset requires any modifications, you'll have to submit a new
+version of your patch. The submission process remains unchanged, except
+for the addition of the version argument to the **git format-patch**
+command.
+
+``` {.sh}
+git format-patch --cover-letter -M origin/main -o outgoing/ -v2
+```
+
+After send a new email with **git send-email**.
+
+------------------------------------------------------------------------
+
+Thanks again and happy hacking!
diff --git a/docs/template.html b/docs/template.html
index 2a1b721..758b4c4 100644
--- a/docs/template.html
+++ b/docs/template.html
@@ -58,6 +58,9 @@
   </header>
   <article>
     <h1>$title$</h1>
+
+    $toc$
+
     $body$
   </article>
   <footer>
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v2] docs: add white mode support
@ 2024-02-17 18:29 Carlos Maniero
  2024-02-17 18:34 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-02-17 18:29 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

For accessibility reasons we now also support white mode.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
v2:
  - Improve color contrast
  - Changes link color for white-mode

 docs/template.html | 110 +++++++++++++++++++++++++--------------------
 1 file changed, 61 insertions(+), 49 deletions(-)

diff --git a/docs/template.html b/docs/template.html
index 98cc348..2a1b721 100644
--- a/docs/template.html
+++ b/docs/template.html
@@ -1,55 +1,67 @@
 <!doctype html>
 <html lang="en">
-  <head>
-    <meta charset="utf-8">
-    <meta name="date" content='$date-meta$'>
-    <title>$title$ | olang</title>
-    <style>
+<head>
+  <meta charset="utf-8">
+  <meta name="date" content='$date-meta$'>
+  <title>$title$ | olang</title>
+  <style>
+    :root {
+      --background-color: #f4f4f4;
+      --text-color: black;
+      --link-color: #4e5289;
+    }
+    @media (prefers-color-scheme: dark) {
       :root {
-        --background-color: #2e3533;
+        --background-color: #2b2b2b;
         --text-color: white;
+        --link-color: #8c91db;
       }
-      body {
-        background: #2e3533;
-        color: white;
-        font-family: monospace;
-        font-size: 1.1rem;
-        margin: 0;
-        padding: 0;
-      }
-      a {
-        color: #8c91db;
-      }
-      article, nav, header, footer {
-        max-width: 820px;
-        margin: 10px auto;
-      }
-      header, footer {
-        margin: 70px auto;
-      }
-      article {
-        line-height: 1.75rem;
-      }
-    </style>
-  </head>
-  <body>
-    <header>
-      <h1>∅lang | The zero programming language.</h1>
-      <nav>
-        <a href="/">Index</a> |
-        <a href="/pages/getting-started.html">Getting started (WIP)</a> |
-        <a href="/pages/hacking.html">Hacking (WIP)</a> |
-        <a href="https://sr.ht/~johnnyrichard/olang/sources" target="_blank">Sources ↗</a> |
-        <a href="https://sr.ht/~johnnyrichard/olang/lists" target="_blank">Mailing list ↗</a> 
-      </nav>
-    </header>
-    <article>
-      <h1>$title$</h1>
-
-      $body$
-    </article>
-    <footer>
-      © 2024 olang maintainers
-    </footer>
-  </body>
+    }
+    body {
+      background: var(--background-color);
+      color: var(--text-color);
+      font-family: monospace;
+      font-size: 1.1rem;
+      margin: 0;
+      padding: 0;
+    }
+    a {
+      color: var(--link-color);
+    }
+    article, nav, header, footer {
+      max-width: 820px;
+      margin: 10px auto;
+    }
+    header, footer {
+      margin: 70px auto;
+    }
+    article {
+      line-height: 1.75rem;
+    }
+    pre {
+      background: rgba(0,0,0, 0.75);
+      color: white;
+      padding: 10px;
+    }
+  </style>
+</head>
+<body>
+  <header>
+    <h1>∅lang | The zero programming language</h1>
+    <nav>
+      <a href="/">Index</a> |
+      <a href="/pages/getting-started.html">Getting started (WIP)</a> |
+      <a href="/pages/hacking.html">Hacking (WIP)</a> |
+      <a href="https://sr.ht/~johnnyrichard/olang/sources" target="_blank">Sources ↗</a> |
+      <a href="https://sr.ht/~johnnyrichard/olang/lists" target="_blank">Mailing list ↗</a>
+    </nav>
+  </header>
+  <article>
+    <h1>$title$</h1>
+    $body$
+  </article>
+  <footer>
+    © 2024 olang maintainers
+  </footer>
+</body>
 </html>
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang] docs: add white-mode support
@ 2024-02-17 17:46 Carlos Maniero
  2024-02-17 17:51 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-02-17 17:46 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

For accessibility reasons we now also support white-mode.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 docs/template.html | 106 +++++++++++++++++++++++++--------------------
 1 file changed, 58 insertions(+), 48 deletions(-)

diff --git a/docs/template.html b/docs/template.html
index 98cc348..ce57b6e 100644
--- a/docs/template.html
+++ b/docs/template.html
@@ -1,55 +1,65 @@
 <!doctype html>
 <html lang="en">
-  <head>
-    <meta charset="utf-8">
-    <meta name="date" content='$date-meta$'>
-    <title>$title$ | olang</title>
-    <style>
+<head>
+  <meta charset="utf-8">
+  <meta name="date" content='$date-meta$'>
+  <title>$title$ | olang</title>
+  <style>
+    :root {
+      --background-color: #f0f0f0;
+      --text-color: black;
+    }
+    @media (prefers-color-scheme: dark) {
       :root {
         --background-color: #2e3533;
         --text-color: white;
       }
-      body {
-        background: #2e3533;
-        color: white;
-        font-family: monospace;
-        font-size: 1.1rem;
-        margin: 0;
-        padding: 0;
-      }
-      a {
-        color: #8c91db;
-      }
-      article, nav, header, footer {
-        max-width: 820px;
-        margin: 10px auto;
-      }
-      header, footer {
-        margin: 70px auto;
-      }
-      article {
-        line-height: 1.75rem;
-      }
-    </style>
-  </head>
-  <body>
-    <header>
-      <h1>∅lang | The zero programming language.</h1>
-      <nav>
-        <a href="/">Index</a> |
-        <a href="/pages/getting-started.html">Getting started (WIP)</a> |
-        <a href="/pages/hacking.html">Hacking (WIP)</a> |
-        <a href="https://sr.ht/~johnnyrichard/olang/sources" target="_blank">Sources ↗</a> |
-        <a href="https://sr.ht/~johnnyrichard/olang/lists" target="_blank">Mailing list ↗</a> 
-      </nav>
-    </header>
-    <article>
-      <h1>$title$</h1>
-
-      $body$
-    </article>
-    <footer>
-      © 2024 olang maintainers
-    </footer>
-  </body>
+    }
+    body {
+      background: var(--background-color);
+      color: var(--text-color);
+      font-family: monospace;
+      font-size: 1.1rem;
+      margin: 0;
+      padding: 0;
+    }
+    a {
+      color: #8c91db;
+    }
+    article, nav, header, footer {
+      max-width: 820px;
+      margin: 10px auto;
+    }
+    header, footer {
+      margin: 70px auto;
+    }
+    article {
+      line-height: 1.75rem;
+    }
+    pre {
+      background: rgba(0,0,0, 0.75);
+      color: white;
+      padding: 10px;
+    }
+  </style>
+</head>
+<body>
+  <header>
+    <h1>∅lang | The zero programming language.</h1>
+    <nav>
+      <a href="/">Index</a> |
+      <a href="/pages/getting-started.html">Getting started (WIP)</a> |
+      <a href="/pages/hacking.html">Hacking (WIP)</a> |
+      <a href="https://sr.ht/~johnnyrichard/olang/sources" target="_blank">Sources ↗</a> |
+      <a href="https://sr.ht/~johnnyrichard/olang/lists" target="_blank">Mailing list ↗</a>
+    </nav>
+  </header>
+  <article>
+    <h1>$title$</h1>
+    $body$
+  </article>
+  <footer>
+    © 2024 olang maintainers
+  </footer>
+</body>
 </html>
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang] docs: add pandoc
@ 2024-02-17 16:22 Carlos Maniero
  2024-02-17 16:27 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-02-17 16:22 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

We replaced the sphinx with pandoc. This commit makes the initial setup
with a basic landing page.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 docs/Makefile                 | 24 +++++++++++++++
 docs/index.md                 | 27 +++++++++++++++++
 docs/pages/getting-started.md |  3 ++
 docs/pages/hacking.md         |  3 ++
 docs/template.html            | 55 +++++++++++++++++++++++++++++++++++
 5 files changed, 112 insertions(+)
 create mode 100644 docs/Makefile
 create mode 100644 docs/index.md
 create mode 100644 docs/pages/getting-started.md
 create mode 100644 docs/pages/hacking.md
 create mode 100644 docs/template.html

diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..e5b7154
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,24 @@
+PANDOC     := pandoc
+INDEX      := index.md
+BUILD_DIR  := build
+TARGET     := $(BUILD_DIR)/index.html
+PAGES_DIR  := pages
+PAGES      := $(wildcard $(PAGES_DIR)/*.md)
+HTML_PAGES := $(patsubst $(PAGES_DIR)/%.md, $(BUILD_DIR)/$(PAGES_DIR)/%.html, $(PAGES))
+
+.PHONY: all
+all:  $(BUILD_DIR) $(TARGET) $(PAGES)
+
+.PHONY: clean
+clean:
+	rm -rf build/
+
+$(TARGET): $(HTML_PAGES)
+	$(PANDOC) -s --template template.html -f markdown -t html $(INDEX) > $(TARGET)
+
+$(BUILD_DIR):
+	@mkdir -p $@
+	@mkdir -p $@/$(PAGES_DIR)
+
+$(BUILD_DIR)/$(PAGES_DIR)/%.html: $(PAGES_DIR)/%.md
+	$(PANDOC) -s --template template.html -f markdown -t html $< > $@
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..b6d5c1a
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,27 @@
+% Welcome to olang documentation
+
+The zero programming language.
+
+## olang manifest
+
+We, as developers, value the modern programming languages that allow us to
+produce reliable code in a short time span. However, we, as programmers, also
+lament that these same languages have diminished the joy of programming by
+imposing unnecessary constraints and complexities on our creative expression.
+
+That is why we are creating a new programming language that may not be suitable
+for most commercial applications, but that can be enjoyed in the places where
+it is meant to be used: on system applications.
+
+olang is a deterministic system language that follows two principles:
+
+- **olang fights complexity** by providing a simple syntax with a low level of abstraction;
+- **olang doesn't babysit programmers** and therefore the compiler only checks semantics.
+
+olang maintainers
+
+### Subscribe olang's development mailing list.
+
+If you want to subscribe to the mailing list, you can achieve it by sending an
+email to
+[~johnnyrichard/olang-devel+subscribe@lists.sr.ht](mailto:~johnnyrichard/olang-devel+subscribe@lists.sr.ht?subject=Subscribe&body=Subscribe)
diff --git a/docs/pages/getting-started.md b/docs/pages/getting-started.md
new file mode 100644
index 0000000..af258df
--- /dev/null
+++ b/docs/pages/getting-started.md
@@ -0,0 +1,3 @@
+% Getting stated (WIP)
+
+WIP
diff --git a/docs/pages/hacking.md b/docs/pages/hacking.md
new file mode 100644
index 0000000..2bb1338
--- /dev/null
+++ b/docs/pages/hacking.md
@@ -0,0 +1,3 @@
+% Hacking WIP
+
+WIP
diff --git a/docs/template.html b/docs/template.html
new file mode 100644
index 0000000..98cc348
--- /dev/null
+++ b/docs/template.html
@@ -0,0 +1,55 @@
+<!doctype html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8">
+    <meta name="date" content='$date-meta$'>
+    <title>$title$ | olang</title>
+    <style>
+      :root {
+        --background-color: #2e3533;
+        --text-color: white;
+      }
+      body {
+        background: #2e3533;
+        color: white;
+        font-family: monospace;
+        font-size: 1.1rem;
+        margin: 0;
+        padding: 0;
+      }
+      a {
+        color: #8c91db;
+      }
+      article, nav, header, footer {
+        max-width: 820px;
+        margin: 10px auto;
+      }
+      header, footer {
+        margin: 70px auto;
+      }
+      article {
+        line-height: 1.75rem;
+      }
+    </style>
+  </head>
+  <body>
+    <header>
+      <h1>∅lang | The zero programming language.</h1>
+      <nav>
+        <a href="/">Index</a> |
+        <a href="/pages/getting-started.html">Getting started (WIP)</a> |
+        <a href="/pages/hacking.html">Hacking (WIP)</a> |
+        <a href="https://sr.ht/~johnnyrichard/olang/sources" target="_blank">Sources ↗</a> |
+        <a href="https://sr.ht/~johnnyrichard/olang/lists" target="_blank">Mailing list ↗</a> 
+      </nav>
+    </header>
+    <article>
+      <h1>$title$</h1>
+
+      $body$
+    </article>
+    <footer>
+      © 2024 olang maintainers
+    </footer>
+  </body>
+</html>
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v2] docs: add sphinx documentation support
@ 2024-02-16 16:24 Johnny Richard
  2024-02-16 15:26 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-02-16 16:24 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

This patch introduces an initial setup for our documentation build.  As
soon as we start writing docs, we can agree on which target should be
added to the root Makefile.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
This version:
- Removes make.bat (we don't plan to run it on Windows).
- Removes modindex on index page. It was responding 404 and we are not
  sure if it works with C project.

 .gitignore     |  1 +
 docs/Makefile  | 20 ++++++++++++++++++++
 docs/conf.py   | 28 ++++++++++++++++++++++++++++
 docs/index.rst | 13 +++++++++++++
 4 files changed, 62 insertions(+)
 create mode 100644 docs/Makefile
 create mode 100644 docs/conf.py
 create mode 100644 docs/index.rst

diff --git a/.gitignore b/.gitignore
index b140d08..77f3000 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
 0c
 build
 *.o
+docs/_build
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..d4bb2cb
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS    ?=
+SPHINXBUILD   ?= sphinx-build
+SOURCEDIR     = .
+BUILDDIR      = _build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..ee7421a
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,28 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# For the full list of built-in configuration values, see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Project information -----------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
+
+project = 'olang'
+copyright = '2024, olang maintainers'
+author = 'olang maintainers'
+release = '0.0.1'
+
+# -- General configuration ---------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
+
+extensions = []
+
+templates_path = ['_templates']
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+
+
+# -- Options for HTML output -------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
+
+html_theme = 'alabaster'
+html_static_path = ['_static']
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..4ad0971
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,13 @@
+Welcome to olang's documentation!
+=================================
+
+.. toctree::
+   :caption: Contents:
+
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`search`
-- 
2.42.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang] docs: build: add deployment script
@ 2024-02-16 16:23 Carlos Maniero
  2024-02-16 16:28 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-02-16 16:23 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 .build.yml | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/.build.yml b/.build.yml
index b38efb3..db47086 100644
--- a/.build.yml
+++ b/.build.yml
@@ -2,9 +2,12 @@ image: archlinux
 packages:
   - gcc
   - make
+  - hut
   - clang
 sources:
   - https://git.sr.ht/~johnnyrichard/olang
+environment:
+  site: johnnyrichard.srht.site
 tasks:
   - lint: |
       cd olang
@@ -15,4 +18,10 @@ tasks:
   - check: |
       cd olang
       make check
+  - docs: |
+      cd olang/docs
+      make html
+      cd _build/html
+      tar -czvf ../docs.tar.gz .
+      hut pages publish -d $site ../docs.tar.gz
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang] docs: add sphinx documentation support
@ 2024-02-16  8:59 Johnny Richard
  2024-02-16  8:01 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Johnny Richard @ 2024-02-16  8:59 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

This patch introduces an initial setup for our documentation build.  As
soon as we start writing docs, we can agree on which target should be
added to the root Makefile.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---

Next step should be documenting the HACKING document and from there we
can have engouh information about dependencies and how to build the
docs.

 .gitignore     |  1 +
 docs/Makefile  | 20 ++++++++++++++++++++
 docs/conf.py   | 28 ++++++++++++++++++++++++++++
 docs/index.rst | 20 ++++++++++++++++++++
 docs/make.bat  | 35 +++++++++++++++++++++++++++++++++++
 5 files changed, 104 insertions(+)
 create mode 100644 docs/Makefile
 create mode 100644 docs/conf.py
 create mode 100644 docs/index.rst
 create mode 100644 docs/make.bat

diff --git a/.gitignore b/.gitignore
index b140d08..77f3000 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
 0c
 build
 *.o
+docs/_build
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..d4bb2cb
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS    ?=
+SPHINXBUILD   ?= sphinx-build
+SOURCEDIR     = .
+BUILDDIR      = _build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..ee7421a
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,28 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# For the full list of built-in configuration values, see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Project information -----------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
+
+project = 'olang'
+copyright = '2024, olang maintainers'
+author = 'olang maintainers'
+release = '0.0.1'
+
+# -- General configuration ---------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
+
+extensions = []
+
+templates_path = ['_templates']
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+
+
+# -- Options for HTML output -------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
+
+html_theme = 'alabaster'
+html_static_path = ['_static']
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..447f3af
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,20 @@
+.. olang documentation master file, created by
+   sphinx-quickstart on Fri Feb 16 09:48:43 2024.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+Welcome to olang's documentation!
+=================================
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Contents:
+
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644
index 0000000..32bb245
--- /dev/null
+++ b/docs/make.bat
@@ -0,0 +1,35 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+	set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=.
+set BUILDDIR=_build
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+	echo.
+	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+	echo.installed, then set the SPHINXBUILD environment variable to point
+	echo.to the full path of the 'sphinx-build' executable. Alternatively you
+	echo.may add the Sphinx directory to PATH.
+	echo.
+	echo.If you don't have Sphinx installed, grab it from
+	echo.https://www.sphinx-doc.org/
+	exit /b 1
+)
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+
+:end
+popd
-- 
2.42.0


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang v3 2/2] tests: add integration test setup
@ 2024-02-16  3:07 Carlos Maniero
  2024-02-16  3:12 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-02-16  3:07 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

This is a basic setup for integration tests which includes:

- a *cli_runner* helper file to provide an interface to interact with
  the CLI.
- a simple test that validates if the compiler is returning zero exit
  code when a file is provided.

At this point the compiler still doing nothing making all this bootstrap
just a fancy way to check if the compiler was compiled properly.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 .build.yml                     |  4 ++
 Makefile                       | 12 ++++++
 tests/integration/Makefile     | 27 ++++++++++++
 tests/integration/cli_runner.c | 77 ++++++++++++++++++++++++++++++++++
 tests/integration/cli_runner.h | 27 ++++++++++++
 tests/integration/cli_test.c   | 39 +++++++++++++++++
 6 files changed, 186 insertions(+)
 create mode 100644 tests/integration/Makefile
 create mode 100644 tests/integration/cli_runner.c
 create mode 100644 tests/integration/cli_runner.h
 create mode 100644 tests/integration/cli_test.c

diff --git a/.build.yml b/.build.yml
index 3aebfcf..b38efb3 100644
--- a/.build.yml
+++ b/.build.yml
@@ -12,3 +12,7 @@ tasks:
   - build: |
       cd olang
       make
+  - check: |
+      cd olang
+      make check
+
diff --git a/Makefile b/Makefile
index 2a23b59..b13b41b 100644
--- a/Makefile
+++ b/Makefile
@@ -19,10 +19,22 @@ $(BUILD_DIR):
 .PHONY: linter
 linter: $(SRCS) $(HEADERS)
 	clang-format --dry-run --Werror $?
+	$(MAKE) -C tests/integration/ linter
+
 
 .PHONY: linter-fix
 linter-fix: $(SRCS) $(HEADERS)
 	clang-format -i $?
+	$(MAKE) -C tests/integration/ linter-fix
+
+.PHONY: integration-test
+integration-test:
+	$(MAKE)
+	$(MAKE) -C tests/integration/
+
+.PHONY: check
+check:
+	$(MAKE) integration-test
 
 $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
 	$(CC) $(CFLAGS) -c $< -o $@
diff --git a/tests/integration/Makefile b/tests/integration/Makefile
new file mode 100644
index 0000000..a42f787
--- /dev/null
+++ b/tests/integration/Makefile
@@ -0,0 +1,27 @@
+SRCS        := $(wildcard *_test.c)
+TO_LINT     := $(filter-out munit.c munit.h,$(wildcard *.c *.h))
+OBJS        := $(patsubst %_test.c, %_test.o, $(SRCS))
+CFLAGS      := -I../../src
+TESTS       := $(patsubst %_test.c, %_test, $(SRCS))
+EXEC_TESTS  := $(patsubst %_test, ./%_test, $(TESTS))
+
+.PHONY: all
+all: munit.o cli_runner.o $(TESTS)
+	@for file in $(EXEC_TESTS); do \
+                ./"$$file"; \
+        done
+
+.PHONY: clean
+clean:
+	$(RM) *.o *_test
+
+.PHONY: linter
+linter: $(TO_LINT)
+	clang-format --dry-run --Werror $?
+
+.PHONY: linter-fix
+linter-fix: $(TO_LINT)
+	clang-format -i $?
+
+cli_test: munit.o cli_runner.o cli_test.o
+	$(CC) $? $(CFLAGS) -o $@
diff --git a/tests/integration/cli_runner.c b/tests/integration/cli_runner.c
new file mode 100644
index 0000000..4e0f7c4
--- /dev/null
+++ b/tests/integration/cli_runner.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#include "cli_runner.h"
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define OLANG_COMPILER_PATH "../../0c"
+
+static int compiler_exists_already_checked = 0;
+
+static void
+assert_compiler_exists()
+{
+    {
+        if (compiler_exists_already_checked == 1) {
+            return;
+        }
+
+        compiler_exists_already_checked = 1;
+    }
+
+    FILE *file = fopen(OLANG_COMPILER_PATH, "r");
+
+    if (file != NULL) {
+        fclose(file);
+        return;
+    }
+
+    perror("Build the compiler before executing tests");
+    exit(1);
+}
+
+void
+create_tmp_file_name(char *file_name)
+{
+    sprintf(file_name, "%s/olang_programXXXXXX", P_tmpdir);
+    int fd = mkstemp(file_name);
+
+    if (fd == -1) {
+        perror("Could not create a tmp file. Check your P_tmpdir permission.");
+        exit(1);
+    }
+    close(fd);
+}
+
+cli_result_t
+cli_runner_compile_file(char *src)
+{
+    assert_compiler_exists();
+
+    cli_result_t result;
+    create_tmp_file_name(result.program_path);
+
+    char command[1024];
+    sprintf(command, "%s -o %s %s", OLANG_COMPILER_PATH, result.program_path, src);
+
+    result.exit_code = system(command);
+    return result;
+}
diff --git a/tests/integration/cli_runner.h b/tests/integration/cli_runner.h
new file mode 100644
index 0000000..5caa319
--- /dev/null
+++ b/tests/integration/cli_runner.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#ifndef CLI_RUNNER_H
+#define CLI_RUNNER_H
+typedef struct cli_result_t
+{
+    int exit_code;
+    char program_path[255];
+} cli_result_t;
+
+cli_result_t
+cli_runner_compile_file(char *src);
+#endif
diff --git a/tests/integration/cli_test.c b/tests/integration/cli_test.c
new file mode 100644
index 0000000..c7a9557
--- /dev/null
+++ b/tests/integration/cli_test.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#define MUNIT_ENABLE_ASSERT_ALIASES
+#include "cli_runner.h"
+#include "munit.h"
+
+static MunitResult
+test_cli_hello_file(const MunitParameter params[], void *user_data_or_fixture)
+{
+    cli_result_t compilation_result = cli_runner_compile_file("../../examples/hello.olang");
+    munit_assert_int(compilation_result.exit_code, ==, 0);
+    return MUNIT_OK;
+}
+
+static MunitTest tests[] = { { "/test_cli_hello_file", test_cli_hello_file, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
+                             { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } };
+
+static const MunitSuite suite = { "/cli_test", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE };
+
+int
+main(int argc, char *argv[])
+{
+    return munit_suite_main(&suite, NULL, argc, argv);
+    return EXIT_SUCCESS;
+}
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang 2/2] tests: add integration test setup
@ 2024-02-15 16:21 Carlos Maniero
  2024-02-15 16:27 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-02-15 16:21 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

This is a basic setup for integration tests which includes:

- a *cli_runner* helper file to provide an interface to interact with
  the CLI.
- a simple test that validates if the compiler is returning zero status
  code when a file is provided.

At this point the compiler still doing nothing making all this bootstrap
just a fancy way to check if the compiler was compiled properly.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 .build.yml                     |  6 ++-
 Makefile                       | 16 ++++++++
 tests/integration/Makefile     | 27 +++++++++++++
 tests/integration/cli_runner.c | 71 ++++++++++++++++++++++++++++++++++
 tests/integration/cli_runner.h | 27 +++++++++++++
 tests/integration/cli_test.c   | 39 +++++++++++++++++++
 6 files changed, 185 insertions(+), 1 deletion(-)
 create mode 100644 tests/integration/Makefile
 create mode 100644 tests/integration/cli_runner.c
 create mode 100644 tests/integration/cli_runner.h
 create mode 100644 tests/integration/cli_test.c

diff --git a/.build.yml b/.build.yml
index 3aebfcf..7ed89eb 100644
--- a/.build.yml
+++ b/.build.yml
@@ -8,7 +8,11 @@ sources:
 tasks:
   - lint: |
       cd olang
-      make linter
+      make linter-all
   - build: |
       cd olang
       make
+  - integration-test: |
+      cd olang
+      make integration-test
+
diff --git a/Makefile b/Makefile
index 2a23b59..fa3df6c 100644
--- a/Makefile
+++ b/Makefile
@@ -20,9 +20,25 @@ $(BUILD_DIR):
 linter: $(SRCS) $(HEADERS)
 	clang-format --dry-run --Werror $?
 
+.PHONY: linter-all
+linter-all: $(SRCS) $(HEADERS)
+	make linter
+	make -C tests/integration/ linter
+
+
 .PHONY: linter-fix
 linter-fix: $(SRCS) $(HEADERS)
 	clang-format -i $?
 
+.PHONY: linter-fix-all
+linter-fix-all: $(SRCS) $(HEADERS)
+	make linter-fix
+	make -C tests/integration/ linter-fix
+
+.PHONY: integration-test
+integration-test:
+	make
+	make -C tests/integration/
+
 $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
 	$(CC) $(CFLAGS) -c $< -o $@
diff --git a/tests/integration/Makefile b/tests/integration/Makefile
new file mode 100644
index 0000000..a42f787
--- /dev/null
+++ b/tests/integration/Makefile
@@ -0,0 +1,27 @@
+SRCS        := $(wildcard *_test.c)
+TO_LINT     := $(filter-out munit.c munit.h,$(wildcard *.c *.h))
+OBJS        := $(patsubst %_test.c, %_test.o, $(SRCS))
+CFLAGS      := -I../../src
+TESTS       := $(patsubst %_test.c, %_test, $(SRCS))
+EXEC_TESTS  := $(patsubst %_test, ./%_test, $(TESTS))
+
+.PHONY: all
+all: munit.o cli_runner.o $(TESTS)
+	@for file in $(EXEC_TESTS); do \
+                ./"$$file"; \
+        done
+
+.PHONY: clean
+clean:
+	$(RM) *.o *_test
+
+.PHONY: linter
+linter: $(TO_LINT)
+	clang-format --dry-run --Werror $?
+
+.PHONY: linter-fix
+linter-fix: $(TO_LINT)
+	clang-format -i $?
+
+cli_test: munit.o cli_runner.o cli_test.o
+	$(CC) $? $(CFLAGS) -o $@
diff --git a/tests/integration/cli_runner.c b/tests/integration/cli_runner.c
new file mode 100644
index 0000000..cd7ba22
--- /dev/null
+++ b/tests/integration/cli_runner.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#include "cli_runner.h"
+#include <assert.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define OLANG_COMPILER_PATH "../../0c"
+
+int _compiler_exists_already_checked = 0;
+
+void
+_assert_compiler_exists()
+{
+    {
+        if (_compiler_exists_already_checked == 1) {
+            return;
+        }
+
+        _compiler_exists_already_checked = 1;
+    }
+
+    FILE *file;
+    if ((file = fopen(OLANG_COMPILER_PATH, "r"))) {
+        fclose(file);
+        return;
+    }
+
+    assert(false && "Compiler not found. Build the compiler before executing tests.");
+}
+
+void
+_create_tmp_file_name(char *file_name)
+{
+    sprintf(file_name, "%s/olang_programXXXXXX", P_tmpdir);
+    int fd = mkstemp(file_name);
+    assert(fd != -1 && "Could not create a tmp file");
+    close(fd);
+}
+
+cli_result_t
+cli_runner_compile_file(char *src)
+{
+    _assert_compiler_exists();
+
+    cli_result_t result;
+    _create_tmp_file_name(result.binary_loc);
+
+    char command[1024];
+    sprintf(command, "%s -o %s %s", OLANG_COMPILER_PATH, result.binary_loc, src);
+
+    result.exit_code = system(command);
+    return result;
+}
diff --git a/tests/integration/cli_runner.h b/tests/integration/cli_runner.h
new file mode 100644
index 0000000..0df7f2d
--- /dev/null
+++ b/tests/integration/cli_runner.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#ifndef CLI_RUNNER_H
+#define CLI_RUNNER_H
+typedef struct cli_result_t
+{
+    int exit_code;
+    char binary_loc[255];
+} cli_result_t;
+
+cli_result_t
+cli_runner_compile_file(char *src);
+#endif
diff --git a/tests/integration/cli_test.c b/tests/integration/cli_test.c
new file mode 100644
index 0000000..c7a9557
--- /dev/null
+++ b/tests/integration/cli_test.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 olang maintainers
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#define MUNIT_ENABLE_ASSERT_ALIASES
+#include "cli_runner.h"
+#include "munit.h"
+
+static MunitResult
+test_cli_hello_file(const MunitParameter params[], void *user_data_or_fixture)
+{
+    cli_result_t compilation_result = cli_runner_compile_file("../../examples/hello.olang");
+    munit_assert_int(compilation_result.exit_code, ==, 0);
+    return MUNIT_OK;
+}
+
+static MunitTest tests[] = { { "/test_cli_hello_file", test_cli_hello_file, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
+                             { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL } };
+
+static const MunitSuite suite = { "/cli_test", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE };
+
+int
+main(int argc, char *argv[])
+{
+    return munit_suite_main(&suite, NULL, argc, argv);
+    return EXIT_SUCCESS;
+}
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread
* [PATCH olang] docs: fix git send-email config instruction
@ 2024-02-13 20:55 Carlos Maniero
  2024-02-13 21:00 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 81+ messages in thread
From: Carlos Maniero @ 2024-02-13 20:55 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 README | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README b/README
index ac992e6..d8ecde9 100644
--- a/README
+++ b/README
@@ -24,7 +24,7 @@ If you want to contribute to the source code, send plain text patch to:
 Please use git-sendmail to send the patches.  You might want to configure your
 git to prevent silly mistakes:
 
-    $ git config sendmail.to "~johnnyrichard/olang-devel@lists.sr.ht"
+    $ git config sendemail.to "~johnnyrichard/olang-devel@lists.sr.ht"
     $ git config format.subjectPrefix "PATCH olang"
 
 ### IRC
-- 
2.34.1


^ permalink raw reply	[flat|nested] 81+ messages in thread

end of thread, other threads:[~2024-09-27 21:11 UTC | newest]

Thread overview: 81+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-08-13 18:55 [PATCH olang v2 0/2] ast: refactor: inline union typedefs Johnny Richard
2024-08-13 18:55 ` [PATCH olang v2 1/2] ast: inline ast_literal_value_t union typedef Johnny Richard
2024-08-13 18:55 ` [PATCH olang v2 2/2] ast: inline ast_node_data_t " Johnny Richard
2024-08-13 18:04   ` [olang/patches/.build.yml] build success builds.sr.ht
2024-08-16  2:30 ` [PATCH olang v2 0/2] ast: refactor: inline union typedefs Carlos Maniero
2024-08-16 22:29   ` Johnny Richard
  -- strict thread matches above, loose matches on Subject: below --
2024-09-27 23:07 [PATCH olang v2 1/2] ast: add function call node Johnny Richard
2024-09-27 21:11 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-09-25 23:20 [PATCH olang v1 2/2] parser: add support for parsing function calls Johnny Richard
2024-09-25 21:22 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-09-25 18:39 [PATCH olang] tests: fix diff error output Carlos Maniero
2024-09-25 18:39 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-09-25 18:30 [PATCH olang] parser: parse multiple function into a single translation unit Carlos Maniero
2024-09-25 18:31 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-09-23 22:19 [PATCH olang v1 2/3] lexer: add token comma Johnny Richard
2024-09-23 22:23 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-09-23 11:43 [PATCH olang 2/2] ast: permit multi declarations on translation unit Carlos Maniero
2024-09-23 11:44 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-09-23 10:11 [PATCH olang v1 3/3] naming: rename all identifier symbols to id Carlos Maniero
2024-09-23 10:12 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-09-22  0:46 [PATCH olang v2 4/4] codegen: operate mov instructions based on the symbol's type Carlos Maniero
2024-09-22  0:47 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-09-21 21:02 [PATCH olang v1 2/2] tests: build: add parallelization support for unit tests Johnny Richard
2024-09-21 21:05 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-09-21  8:25 [PATCH olang 5/5] codegen: perform mov instructions based on variable type Carlos Maniero
2024-09-21  8:26 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-09-21  1:13 [PATCH olang 5/5] codegen: preserve function's variable stack location Carlos Maniero
2024-09-21  1:13 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-09-21  0:20 [PATCH olang v1 3/3] codegen: add support scopes and symbols lookups for var Johnny Richard
2024-09-21  0:23 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-09-17 15:14 [PATCH olang] cli: add libc error handling Carlos Maniero
2024-09-17 15:15 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-09-17 13:43 [PATCH olang v1] remove unused examples programs Johnny Richard
2024-09-17 11:43 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-09-17 12:46 [PATCH olang v1 4/4] docs: info: add instructions to install/uninstall olang Johnny Richard
2024-09-17 10:48 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-09-16 16:29 [PATCH olang v1 3/3] docs: remove pandoc dependency for man docs Johnny Richard
2024-09-16 14:31 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-09-11  1:03 [PATCH olang v1 2/2] parser: add var definition and reference support Johnny Richard
2024-09-10 23:05 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-08-25 13:16 [PATCH olang v2 2/2] codegen: x86_64: implement binary operations Johnny Richard
2024-08-25 13:26 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-08-21  3:39 [PATCH olang 1/2] tests: add comment based integration tests mechanism Carlos Maniero
2024-08-21  3:41 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-05-12 14:30 [PATCH olang 4/4] tests: print integration tests TODOs Carlos Maniero
2024-05-12 14:31 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-04-27 12:14 [PATCH olang v1 2/2] codegen: x86_64: implement binary operations Johnny Richard
2024-04-27 11:21 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-04-18 23:08 [PATCH olang v1] parser: fix parse expression with binop chain Johnny Richard
2024-04-18 22:11 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-04-18 22:18 [PATCH olang v1] parser: add missing <= and >= binary operators Johnny Richard
2024-04-18 21:22 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-04-18 21:58 [PATCH olang v1] docs: spec: add %, <= and >= binary operators Johnny Richard
2024-04-18 21:02 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-04-16 23:51 [PATCH olang v1] Revert "docs: spec: postpone assignment operators" Johnny Richard
2024-04-16 22:56 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-04-16 23:35 [PATCH olang v2] docs: spec: add binary expressions Johnny Richard
2024-04-16 22:40 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-04-15 18:20 [PATCH olang v1] spec: ebnf: add binary expressions Johnny Richard
2024-04-15 17:43 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-04-08  4:38 [PATCH olang v2 2/2] docs: spec: add variables and constants specification Carlos Maniero
2024-04-08  4:39 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-03-29  1:59 [PATCH olang] linter: turn off clang-format to keep retro compatibility with v16 Johnny Richard
2024-03-29  0:59 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-03-29  0:33 [PATCH olang] site: change look and feel and rewrite home introduction section Johnny Richard
2024-03-28 23:33 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-03-24 16:12 [PATCH olang v3] docs: create o programming language spec Johnny Richard
2024-03-24 15:16 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-03-19 20:18 [PATCH olang v2] docs: create o programming language spec Johnny Richard
2024-03-19 19:20 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-03-19 19:57 [PATCH olang v1 3/3] codegen: add compiler support to linux aarch64 arch Johnny Richard
2024-03-19 19:00 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-03-18  8:39 [PATCH olang v3 3/3] parser: add all binary operation expressions Johnny Richard
2024-03-18  7:43 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-03-17 21:29 [PATCH olang v2 3/3] parser: add all binary operation expressions Johnny Richard
2024-03-17 20:37 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-03-13 21:21 [PATCH olang v1 3/3] parser: add basic arithmetic expressions '+' '*' '/' '-' Johnny Richard
2024-03-13 20:29 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-03-13 12:44 [PATCH olang v3] refactor: rename zero programming language to olang Fabio Maciel
2024-03-13 12:45 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-03-12 19:35 [PATCH olang v1] refactor: rename zero programming language to olang Johnny Richard
2024-03-12 18:40 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-03-11  8:48 [PATCH olang] site: change dns to o-lang.org Johnny Richard
2024-03-11  7:50 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-03-09  0:05 [RFC PATCH olang v1] docs: create zero programming language specification Johnny Richard
2024-03-08 23:09 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-03-14  4:29   ` Ricardo Kagawa
2024-03-14 22:43     ` Johnny Richard
2024-03-08 23:13 [PATCH olang v1] ast: add ast_node root for the entire program Johnny Richard
2024-03-08 22:13 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-03-08 22:39 [PATCH olang v2 3/3] tests: add tests for the minimal possible olang program Carlos Maniero
2024-03-08 22:40 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-03-07 23:23 [PATCH olang 3/3] tests: add tests for the minimal possible olang program Carlos Maniero
2024-03-07 23:24 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-03-01 22:24 [PATCH olang v2 4/4] parser: create simplified parser for tiny AST Johnny Richard
2024-03-01 21:32 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-28 19:04 [PATCH olang v1 4/4] parser: create simplified parser for tiny AST Johnny Richard
2024-02-28 18:11 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-28 14:25 [PATCH olang v3] arena: optimization: ensure alignment memory access Carlos Maniero
2024-02-28 14:26 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-28 12:37 [PATCH olang v2] cli: replace memory allocation malloc -> arena Johnny Richard
2024-02-28 11:39 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-27 19:59 [PATCH olang v2 2/2] utils: create hash map data structure Johnny Richard
2024-02-27 19:01 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-24 20:40 [PATCH olang] test: fix suite name for list_test and arena_test Johnny Richard
2024-02-24 19:42 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-22 19:09 [PATCH olang] cli: replace memory allocation malloc -> arena Johnny Richard
2024-02-22 18:11 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-22 18:38 [PATCH olang] docs: add DCO information on hacking page Johnny Richard
2024-02-22 17:41 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-22 18:24 [PATCH olang] build: rename 0c.c file to main.c Johnny Richard
2024-02-22 17:26 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-21 22:20 [PATCH olang 2/2] utils: create hash map data structure Johnny Richard
2024-02-21 21:24 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-21 15:09 [PATCH olang v2] arena: optimization: make arena 8 bits aligned Carlos Maniero
2024-02-21 15:09 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-21  5:52 [PATCH olang] arena: optimization: make arena 8 bits aligned Carlos Maniero
2024-02-21  5:53 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-20 23:37 [PATCH olang] utils: add linked-list Carlos Maniero
2024-02-20 23:37 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-20 17:35 [PATCH olang v3] utils: add arena Carlos Maniero
2024-02-20 17:41 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-19 20:42 [PATCH olang v5 4/4] lexer: test: add integration tests for --dump-tokens Carlos Maniero
2024-02-19 20:48 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-19  1:44 [PATCH olang v3 2/2] lexer: create --dump-tokens cli command Johnny Richard
2024-02-19  0:47 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-18  0:50 [PATCH olang 2/2] tests: add unit tests configuration Carlos Maniero
2024-02-18  0:55 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-17 21:04 [PATCH olang] docs: deploy: replace shrt.site domain by olang.johnnyrichard.com Johnny Richard
2024-02-17 20:03 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-17 20:38 [PATCH olang] docs: build: fix docs publishing task Johnny Richard
2024-02-17 19:37 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-17 20:12 [PATCH olang] docs: add mobile version Carlos Maniero
2024-02-17 20:17 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-17 18:40 [PATCH olang v2] docs: add HACKING documentation Carlos Maniero
2024-02-17 18:45 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-17 18:29 [PATCH olang v2] docs: add white mode support Carlos Maniero
2024-02-17 18:34 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-17 17:46 [PATCH olang] docs: add white-mode support Carlos Maniero
2024-02-17 17:51 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-17 16:22 [PATCH olang] docs: add pandoc Carlos Maniero
2024-02-17 16:27 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-16 16:24 [PATCH olang v2] docs: add sphinx documentation support Johnny Richard
2024-02-16 15:26 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-16 16:23 [PATCH olang] docs: build: add deployment script Carlos Maniero
2024-02-16 16:28 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-16  8:59 [PATCH olang] docs: add sphinx documentation support Johnny Richard
2024-02-16  8:01 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-16  3:07 [PATCH olang v3 2/2] tests: add integration test setup Carlos Maniero
2024-02-16  3:12 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-15 16:21 [PATCH olang 2/2] tests: add integration test setup Carlos Maniero
2024-02-15 16:27 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-02-13 20:55 [PATCH olang] docs: fix git send-email config instruction Carlos Maniero
2024-02-13 21:00 ` [olang/patches/.build.yml] build success builds.sr.ht

Code repositories for project(s) associated with this public inbox

	https://git.johnnyrichard.com/olang.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox