public inbox for ~johnnyrichard/olang-devel@lists.sr.ht
 help / color / mirror / code / Atom feed
* [PATCH olang] ast: add token location at the ast nodes
@ 2024-10-04 23:02 Carlos Maniero
  2024-10-04 23:03 ` [olang/patches/.build.yml] build success builds.sr.ht
  2024-10-05  1:04 ` [PATCH olang] ast: add token location at the ast nodes Johnny Richard
  0 siblings, 2 replies; 124+ messages in thread
From: Carlos Maniero @ 2024-10-04 23:02 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

This is an important step for future semantics error reporting and
binary debug information.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 src/ast.c    | 29 +++++++++++++++++++--------
 src/ast.h    | 55 ++++++++++++++++++++++++++++++++++------------------
 src/parser.c | 29 ++++++++++++++-------------
 3 files changed, 73 insertions(+), 40 deletions(-)

diff --git a/src/ast.c b/src/ast.c
index db18426..1224305 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -40,7 +40,12 @@ 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_new_node_fn_def(arena_t *arena,
+                    token_loc_t loc,
+                    string_view_t id,
+                    list_t *params,
+                    string_view_t return_type,
+                    ast_node_t *block)
 {
     assert(arena);
     assert(params);
@@ -50,6 +55,7 @@ ast_new_node_fn_def(arena_t *arena, string_view_t id, list_t *params, string_vie
     assert(node_fn_def);
 
     node_fn_def->kind = AST_NODE_FN_DEF;
+    node_fn_def->loc = loc;
     ast_fn_definition_t *fn_def = &node_fn_def->as_fn_def;
 
     fn_def->id = id;
@@ -61,7 +67,7 @@ ast_new_node_fn_def(arena_t *arena, string_view_t id, list_t *params, string_vie
 }
 
 ast_node_t *
-ast_new_node_fn_call(arena_t *arena, string_view_t id, list_t *args)
+ast_new_node_fn_call(arena_t *arena, token_loc_t loc, string_view_t id, list_t *args)
 {
     assert(arena);
     assert(args);
@@ -70,6 +76,7 @@ ast_new_node_fn_call(arena_t *arena, string_view_t id, list_t *args)
     assert(node_fn_call);
 
     node_fn_call->kind = AST_NODE_FN_CALL;
+    node_fn_call->loc = loc;
     ast_fn_call_t *fn_call = &node_fn_call->as_fn_call;
 
     fn_call->id = id;
@@ -79,12 +86,13 @@ 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)
+ast_new_node_var_def(arena_t *arena, token_loc_t loc, 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);
 
     node_var_def->kind = AST_NODE_VAR_DEF;
+    node_var_def->loc = loc;
     ast_var_definition_t *var_def = &node_var_def->as_var_def;
 
     var_def->id = id;
@@ -95,12 +103,13 @@ ast_new_node_var_def(arena_t *arena, string_view_t id, string_view_t type, ast_n
 }
 
 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_new_node_bin_op(arena_t *arena, token_loc_t loc, ast_binary_op_kind_t kind, ast_node_t *lhs, ast_node_t *rhs)
 {
     ast_node_t *node_bin_op = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
     assert(node_bin_op);
 
     node_bin_op->kind = AST_NODE_BINARY_OP;
+    node_bin_op->loc = loc;
     node_bin_op->as_bin_op.kind = kind;
     node_bin_op->as_bin_op.lhs = lhs;
     node_bin_op->as_bin_op.rhs = rhs;
@@ -109,12 +118,13 @@ ast_new_node_bin_op(arena_t *arena, ast_binary_op_kind_t kind, ast_node_t *lhs,
 }
 
 ast_node_t *
-ast_new_node_literal_u32(arena_t *arena, uint32_t value)
+ast_new_node_literal_u32(arena_t *arena, token_loc_t loc, 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->loc = loc;
     node_literal->as_literal.kind = AST_LITERAL_U32;
     node_literal->as_literal.as_u32 = value;
 
@@ -122,36 +132,39 @@ ast_new_node_literal_u32(arena_t *arena, uint32_t value)
 }
 
 ast_node_t *
-ast_new_node_ref(arena_t *arena, string_view_t id)
+ast_new_node_ref(arena_t *arena, token_loc_t loc, 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->loc = loc;
     node_ref->as_ref.id = id;
 
     return node_ref;
 }
 
 ast_node_t *
-ast_new_node_return_stmt(arena_t *arena, ast_node_t *expr)
+ast_new_node_return_stmt(arena_t *arena, token_loc_t loc, ast_node_t *expr)
 {
     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;
+    node_return_stmt->loc = loc;
     node_return_stmt->as_return_stmt.expr = expr;
 
     return node_return_stmt;
 }
 
 ast_node_t *
-ast_new_node_if_stmt(arena_t *arena, ast_node_t *cond, ast_node_t *then, ast_node_t *_else)
+ast_new_node_if_stmt(arena_t *arena, token_loc_t loc, ast_node_t *cond, ast_node_t *then, ast_node_t *_else)
 {
     ast_node_t *node_if_stmt = arena_alloc(arena, sizeof(ast_node_t));
     assert(node_if_stmt);
 
     node_if_stmt->kind = AST_NODE_IF_STMT;
+    node_if_stmt->loc = loc;
     node_if_stmt->as_if_stmt.cond = cond;
     node_if_stmt->as_if_stmt.then = then;
     node_if_stmt->as_if_stmt._else = _else;
diff --git a/src/ast.h b/src/ast.h
index 4791d6b..f9a23b5 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -20,6 +20,7 @@
 #include <stdint.h>
 
 #include "arena.h"
+#include "lexer.h"
 #include "list.h"
 #include "scope.h"
 #include "string_view.h"
@@ -42,15 +43,21 @@ typedef enum
     AST_NODE_UNKNOWN
 } ast_node_kind_t;
 
+typedef struct ast_node_meta
+{
+    ast_node_kind_t kind;
+    token_loc_t loc;
+} ast_node_meta_t;
+
 typedef struct ast_block
 {
-    ast_node_kind_t node_kind;
+    ast_node_meta_t meta;
     list_t *nodes;
 } ast_block_t;
 
 typedef struct ast_translation_unit
 {
-    ast_node_kind_t node_kind;
+    ast_node_meta_t meta;
     list_t *decls;
 } ast_translation_unit_t;
 
@@ -62,7 +69,7 @@ typedef struct ast_fn_param
 
 typedef struct ast_fn_definition
 {
-    ast_node_kind_t node_kind;
+    ast_node_meta_t meta;
     string_view_t id;
     list_t *params;
     string_view_t return_type;
@@ -72,7 +79,7 @@ typedef struct ast_fn_definition
 
 typedef struct ast_fn_call
 {
-    ast_node_kind_t node_kind;
+    ast_node_meta_t meta;
     string_view_t id;
     list_t *args;
     scope_t *scope;
@@ -80,7 +87,7 @@ typedef struct ast_fn_call
 
 typedef struct ast_var_definition
 {
-    ast_node_kind_t node_kind;
+    ast_node_meta_t meta;
     string_view_t id;
     string_view_t type;
     ast_node_t *value;
@@ -94,7 +101,7 @@ typedef enum
 
 typedef struct ast_literal
 {
-    ast_node_kind_t node_kind;
+    ast_node_meta_t meta;
     ast_literal_kind_t kind;
     union
     {
@@ -104,7 +111,7 @@ typedef struct ast_literal
 
 typedef struct ast_ref
 {
-    ast_node_kind_t node_kind;
+    ast_node_meta_t meta;
     string_view_t id;
     scope_t *scope;
 } ast_ref_t;
@@ -133,7 +140,7 @@ typedef enum ast_binary_op_kind
 
 typedef struct ast_binary_op
 {
-    ast_node_kind_t node_kind;
+    ast_node_meta_t meta;
     ast_binary_op_kind_t kind;
     ast_node_t *lhs;
     ast_node_t *rhs;
@@ -141,13 +148,13 @@ typedef struct ast_binary_op
 
 typedef struct ast_return_stmt
 {
-    ast_node_kind_t node_kind;
+    ast_node_meta_t meta;
     ast_node_t *expr;
 } ast_return_stmt_t;
 
 typedef struct ast_if_stmt
 {
-    ast_node_kind_t node_kind;
+    ast_node_meta_t meta;
     ast_node_t *cond;
     ast_node_t *then;
     ast_node_t *_else;
@@ -155,7 +162,12 @@ typedef struct ast_if_stmt
 
 typedef union ast_node
 {
-    ast_node_kind_t kind;
+    // inlined ast_node_meta_t struct.
+    struct
+    {
+        ast_node_kind_t kind;
+        token_loc_t loc;
+    };
     ast_translation_unit_t as_translation_unit;
     ast_fn_definition_t as_fn_def;
     ast_fn_call_t as_fn_call;
@@ -172,28 +184,33 @@ ast_node_t *
 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_new_node_fn_def(arena_t *arena,
+                    token_loc_t loc,
+                    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_new_node_fn_call(arena_t *arena, token_loc_t loc, 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);
+ast_new_node_var_def(arena_t *arena, token_loc_t loc, 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);
+ast_new_node_bin_op(arena_t *arena, token_loc_t loc, 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_new_node_literal_u32(arena_t *arena, token_loc_t loc, uint32_t value);
 
 ast_node_t *
-ast_new_node_ref(arena_t *arena, string_view_t id);
+ast_new_node_ref(arena_t *arena, token_loc_t loc, string_view_t id);
 
 ast_node_t *
-ast_new_node_return_stmt(arena_t *arena, ast_node_t *expr);
+ast_new_node_return_stmt(arena_t *arena, token_loc_t loc, ast_node_t *expr);
 
 ast_node_t *
-ast_new_node_if_stmt(arena_t *arena, ast_node_t *cond, ast_node_t *then, ast_node_t *_else);
+ast_new_node_if_stmt(arena_t *arena, token_loc_t loc, ast_node_t *cond, ast_node_t *then, ast_node_t *_else);
 
 ast_node_t *
 ast_new_node_block(arena_t *arena);
diff --git a/src/parser.c b/src/parser.c
index ecc10f0..35c8107 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -224,7 +224,7 @@ parser_parse_expr_1(parser_t *parser, ast_node_t *lhs, size_t prev_precedence)
             lexer_peek_next(parser->lexer, &lookahead_token);
         }
 
-        lhs = ast_new_node_bin_op(parser->arena, token_kind_to_binary_op_kind(token_op.kind), lhs, rhs);
+        lhs = ast_new_node_bin_op(parser->arena, token_op.loc, token_kind_to_binary_op_kind(token_op.kind), lhs, rhs);
         if (lhs == NULL) {
             return NULL;
         }
@@ -252,19 +252,19 @@ parser_parse_factor(parser_t *parser)
 
     switch (token.kind) {
         case TOKEN_NUMBER:
-            return ast_new_node_literal_u32(parser->arena, string_view_to_u32(token.value));
+            return ast_new_node_literal_u32(parser->arena, token.loc, string_view_to_u32(token.value));
 
         case TOKEN_ID: {
-            string_view_t id = token.value;
+            token_t token_id = token;
 
             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_fn_call(parser->arena, token_id.loc, token_id.value, args);
             }
 
-            return ast_new_node_ref(parser->arena, id);
+            return ast_new_node_ref(parser->arena, token_id.loc, token_id.value);
         }
 
         case TOKEN_OPAREN: {
@@ -411,7 +411,7 @@ parser_parse_fn_definition(parser_t *parser)
         return NULL;
     }
 
-    return ast_new_node_fn_def(parser->arena, fn_name_token.value, params, fn_return_type, block);
+    return ast_new_node_fn_def(parser->arena, fn_name_token.loc, fn_name_token.value, params, fn_return_type, block);
 }
 
 static bool
@@ -498,7 +498,9 @@ EndLoop:
 static ast_node_t *
 parser_parse_return_stmt(parser_t *parser)
 {
-    if (!skip_expected_token(parser, TOKEN_RETURN)) {
+    token_t token_ret;
+
+    if (!expected_next_token(parser, &token_ret, TOKEN_RETURN)) {
         return NULL;
     }
 
@@ -507,7 +509,7 @@ parser_parse_return_stmt(parser_t *parser)
         return NULL;
     }
 
-    ast_node_t *node_return_stmt = ast_new_node_return_stmt(parser->arena, expr);
+    ast_node_t *node_return_stmt = ast_new_node_return_stmt(parser->arena, token_ret.loc, expr);
     assert(node_return_stmt);
 
     if (!skip_expected_token(parser, TOKEN_LF)) {
@@ -521,7 +523,8 @@ parser_parse_return_stmt(parser_t *parser)
 static ast_node_t *
 parser_parse_if_stmt(parser_t *parser)
 {
-    if (!skip_expected_token(parser, TOKEN_IF)) {
+    token_t token_if;
+    if (!expected_next_token(parser, &token_if, TOKEN_IF)) {
         return NULL;
     }
 
@@ -557,7 +560,7 @@ parser_parse_if_stmt(parser_t *parser)
         return NULL;
     }
 
-    ast_node_t *node_if_stmt = ast_new_node_if_stmt(parser->arena, cond, then, _else);
+    ast_node_t *node_if_stmt = ast_new_node_if_stmt(parser->arena, token_if.loc, cond, then, _else);
 
     assert(node_if_stmt);
 
@@ -573,8 +576,8 @@ parser_parse_var_def(parser_t *parser)
         return NULL;
     }
 
-    token_t id_token;
-    if (!expected_next_token(parser, &id_token, TOKEN_ID)) {
+    token_t token_id;
+    if (!expected_next_token(parser, &token_id, TOKEN_ID)) {
         return NULL;
     }
 
@@ -593,7 +596,7 @@ parser_parse_var_def(parser_t *parser)
         return NULL;
     }
 
-    ast_node_t *var_node = ast_new_node_var_def(parser->arena, id_token.value, var_type, expr);
+    ast_node_t *var_node = ast_new_node_var_def(parser->arena, token_id.loc, token_id.value, var_type, expr);
 
     skip_line_feeds(parser->lexer);
 

base-commit: 832f13d2ed2762bb9582eb1b633a30af608e028f
-- 
2.34.1


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1] docs: add code highlight post processor
@ 2024-11-08  4:24 Carlos Maniero
  2024-11-08  4:24 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-11-08  4:24 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

This a very simple token based code highlighter that is not able to
evaluate syntax.

How it works:

The @sethl inserts a html comment at generated html. The generated html
file is than post processed by the script which highlights the code.

The alternative is to use the texi2any *HIGHLIGHT_SYNTAX* [1] experimental
feature, which would require the inclusion of other dependency to the
project.

[1]: https://www.gnu.org/software/texinfo/manual/texinfo/html_node/Syntax-Highlighting.html

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 docs/info/Makefile             |   1 +
 docs/info/_ext.texi            |  15 +++++
 docs/info/c-ffi.texi           |  17 +++++-
 docs/info/codehl.py            | 104 +++++++++++++++++++++++++++++++++
 docs/info/getting-started.texi |  32 +++++++++-
 docs/info/specification.texi   |   3 +
 docs/style.css                 |  92 ++++++++++++++++++++++++++---
 7 files changed, 250 insertions(+), 14 deletions(-)
 create mode 100644 docs/info/_ext.texi
 create mode 100644 docs/info/codehl.py

diff --git a/docs/info/Makefile b/docs/info/Makefile
index b2d29a4..12cb790 100644
--- a/docs/info/Makefile
+++ b/docs/info/Makefile
@@ -6,6 +6,7 @@ all: html
 
 html: olang.texi introduction.texi installation.texi getting-started.texi specification.texi contribution-guide.texi
 	$(MAKEINFO) --set-customization-variable AFTER_BODY_OPEN="$(HTML_HEADER)" --css-include=../style.css -o html --html olang.texi
+	find html -name '*.html' | xargs python3 codehl.py
 
 .PHONY: clean
 clean:
diff --git a/docs/info/_ext.texi b/docs/info/_ext.texi
new file mode 100644
index 0000000..2a0d6e7
--- /dev/null
+++ b/docs/info/_ext.texi
@@ -0,0 +1,15 @@
+@ifclear olc_info_ext
+@macro sethl{lang}
+@html
+<!--syntax:\lang\-->
+@end html
+@end macro
+
+@macro sethl-{lang}
+@html
+<!--syntax:\lang\ no-numbers-->
+@end html
+@end macro
+@end ifclear
+
+@set olc_info_ext
diff --git a/docs/info/c-ffi.texi b/docs/info/c-ffi.texi
index 719fcd5..12ba9d0 100644
--- a/docs/info/c-ffi.texi
+++ b/docs/info/c-ffi.texi
@@ -1,3 +1,5 @@
+@include _ext.texi
+
 @node C FFI
 @chapter C FFI
 
@@ -13,6 +15,7 @@ To call external code you can use the @code{extern} statement.
 
 @subsection extern
 
+@sethl olang
 @verbatim
 # file: answer.ol
 
@@ -28,6 +31,7 @@ fn main(): u8 {
 That way instead of defining the @code{u32print} function. It must be provided
 at the linkage step.
 
+@sethl c
 @verbatim
 // file: u32print.c
 
@@ -40,10 +44,11 @@ int u32print(int number) {
 
 @subsection Compiling without linking
 
-If you try to compile the answer.ol file you will receive the follow error:
+If you try to compile the answer.ol file by using the
+@code{olc answer.ol -o answer.o} command you will receive the follow error:
 
+@sethl- output
 @verbatim
-olc answer.ol -o answer.o
 /usr/bin/ld: answer.o.o: in function `main':
 (.text+0x10): undefined reference to `u32print'
 collect2: error: ld returned 1 exit status
@@ -52,6 +57,7 @@ collect2: error: ld returned 1 exit status
 That's because @code{O} tries to link by default. To assemble the code without
 linking it, you can use the @code{-c} option.
 
+@sethl- bash
 @verbatim
 olc answer.ol -c -o answer.o
 @end verbatim
@@ -59,12 +65,14 @@ olc answer.ol -c -o answer.o
 After that you can do the same with the C file that contains the function that
 you are looking to call.
 
+@sethl- bash
 @verbatim
 gcc u32print.c -c -o u32print.o
 @end verbatim
 
 And then you can link both object files into an executable:
 
+@sethl- bash
 @verbatim
 gcc answer.o u32print.o -o answer
 @end verbatim
@@ -73,6 +81,7 @@ gcc answer.o u32print.o -o answer
 
 All that is required is to set the function prototype.
 
+@sethl olang
 @verbatim
 # file: sum.ol
 
@@ -81,8 +90,9 @@ fn sum(a: u32, b: u32): u32 {
 }
 @end verbatim
 
+@sethl c
 @verbatim
-# file: csum.c
+// file: csum.c
 
 #include <stdio.h>
 
@@ -96,6 +106,7 @@ int main() {
 
 Compiling:
 
+@sethl- bash
 @verbatim
 olc sum.ol -c -o sum.o
 gcc sum.o csum.c -o csum
diff --git a/docs/info/codehl.py b/docs/info/codehl.py
new file mode 100644
index 0000000..0749138
--- /dev/null
+++ b/docs/info/codehl.py
@@ -0,0 +1,104 @@
+# Copyright (C) 2024 Carlos Maniero<carlos@maniero.me>
+#
+# 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/>.
+import sys
+import re
+
+re_hl_block = r'<!--syntax:(?P<lang>\w+)( (?P<class>.*?))?-->.*?<pre class="verbatim">(?P<code>.*?)<\/pre>'
+
+lexers = {
+    'default': [
+        (r'^(?P<value> +)', None),
+        (r'^(?P<value>\d+)', 'num'),
+        (r'^(?P<value>\w+)', 'ident'),
+        (r'^(?P<value>\(|\)|{|})', 'brac'),
+        (r'^(?P<value>&\w+;|\^|\~|!|@|\#|\$|%|&|\*|-|\+|\.|=|,|:|;|\||\\|/|\?)', 'symbol'),
+        (r'^(?P<value>.+)', None),
+    ],
+    'olang': [
+        (r'^(?P<value>#.*)', 'comment'),
+        (r'^(?P<value>\w+) *?\(', 'fn-name'),
+        (r'^(?P<value>fn|if|else|return|while|var|const|extern)', 'key'),
+        (r'^\b(?P<value>u8|u16|u32|u64)\b', 'type'),
+    ],
+    'c': [
+        (r'^(?P<value>#.*)', 'macro'),
+        (r'^(?P<value>&quot;.*?&quot;)', 'str'),
+        (r'^(?P<value>//.*)', 'comment'),
+        (r'^(?P<value>\w+) *?\(', 'fn-name'),
+        (r'^(?P<value>fn|if|else|return|while|var|const|extern)', 'key'),
+        (r'^\b(?P<value>int|long|float)\b', 'type'),
+    ],
+    'bash': [
+        (r'^(?P<value>-+.+? ?)', 'opt'),
+        (r'^(?P<value>[A-z0\d\.-/]+)', 'ident'),
+    ],
+    'output': [
+        (r'(?P<value>.*)', None),
+    ],
+    'ebnf': [
+        (r'^(?P<value>.+?::=)', 'fn-name'),
+        (r'^(?P<value>\|)', 'fn-name'),
+        (r'^(?P<value>\?|\*)', 'key'),
+        (r'^(?P<value>\(\*.*?\*\))', 'comment'),
+        (r'^(?P<value>\'.*?\')', 'str'),
+    ]
+}
+
+def format_code_line(line, lang):
+    new_line = ''
+
+    lexer = lexers.get(lang, []) + lexers['default']
+
+    while line != '':
+        for (regex, name) in lexer:
+            match = re.match(regex, line)
+            if match:
+                value = match.group('value')
+                line = line[len(value):]
+                if name:
+                    new_line += f'<span class="code-{name}">{value}</span>'
+                else:
+                    new_line += value
+                break
+
+
+    return f'<span class="code-line">{new_line}</span>'
+
+def format_pre(match):
+    lang = match.group('lang')
+    css_class = match.group('class')
+    css_classes = f'code-hl lang-{lang} {css_class or ""}'.strip()
+    raw_lines = match.group('code').split('\n')
+
+    while raw_lines[-1].strip() == '':
+        raw_lines = raw_lines[:-1]
+
+    code = "\n".join([format_code_line(line, lang) for line in raw_lines])
+
+    return f'<pre class="{css_classes}">{code}</pre>'
+
+def codehl(code):
+    return re.sub(re_hl_block, format_pre, code, flags=re.DOTALL)
+
+if __name__ == '__main__':
+    for file_name in sys.argv[1:]:
+        print(f"HL\t{file_name}")
+
+        with open(file_name, 'r+') as file:
+            content = codehl(file.read())
+
+            file.seek(0)
+            file.write(content)
+            file.truncate()
diff --git a/docs/info/getting-started.texi b/docs/info/getting-started.texi
index 77b9298..75c00f0 100644
--- a/docs/info/getting-started.texi
+++ b/docs/info/getting-started.texi
@@ -1,3 +1,5 @@
+@include _ext.texi
+
 @node Getting Started
 @chapter Getting Started
 
@@ -15,6 +17,7 @@ much the same with some small quirks. Let's dive in!
 An O programming language program starts with a @code{main} function. This
 function must return the program exit code.
 
+@sethl olang
 @verbatim
 fn main(): u8 {
   return 0
@@ -23,9 +26,10 @@ fn main(): u8 {
 
 To compile the program you can use @code{olc}.
 
+@sethl- bash
 @verbatim
-$ olc my_prog.ol -o my_prog
-$ ./my_prog
+olc my_prog.ol -o my_prog
+./my_prog
 @end verbatim
 
 @section Functions
@@ -34,6 +38,7 @@ Unlike C, O language does not require function prototypes. This means you can
 call a function before it's defined, making your code more flexible and easier
 to read in many cases.
 
+@sethl olang
 @verbatim
 fn main(): u8 {
   return fib(8)
@@ -56,12 +61,14 @@ fn fib(n: u32): u32 {
 
 Comments starts with a @code{#}.
 
+@sethl- olang
 @verbatim
 # Hi I'm a comment and I'll be ignored by the compiler.
 @end verbatim
 
 @section Variables
 
+@sethl- olang
 @verbatim
 var answer: u32 = 42
 @end verbatim
@@ -72,6 +79,7 @@ Any non zero expr is true.
 
 @subsection If-Else
 
+@sethl olang
 @verbatim
 if expr {
   # statement
@@ -84,6 +92,7 @@ if expr {
 
 While loop
 
+@sethl olang
 @verbatim
 while expr {
   # statement
@@ -92,6 +101,7 @@ while expr {
 
 @subsection While loop
 
+@sethl olang
 @verbatim
 while expr {
   # statement
@@ -128,6 +138,7 @@ Unsigned 64 bits.
 
 @item Equals
 
+@sethl- olang
 @verbatim
 expr1 == expr2
 @end verbatim
@@ -136,6 +147,7 @@ Results zero (false) or one (true).
 
 @item Less
 
+@sethl- olang
 @verbatim
 expr1 < expr2
 @end verbatim
@@ -144,6 +156,7 @@ Results zero (false) or one (true).
 
 @item Less Equal
 
+@sethl- olang
 @verbatim
 expr1 <= expr2
 @end verbatim
@@ -152,6 +165,7 @@ Results zero (false) or one (true).
 
 @item Greater
 
+@sethl- olang
 @verbatim
 expr1 > expr2
 @end verbatim
@@ -160,6 +174,7 @@ Results zero (false) or one (true).
 
 @item Greater Equal
 
+@sethl- olang
 @verbatim
 expr1 >= expr2
 @end verbatim
@@ -168,6 +183,7 @@ Results zero (false) or one (true).
 
 @item Or
 
+@sethl- olang
 @verbatim
 expr1 || expr2
 @end verbatim
@@ -176,6 +192,7 @@ Results zero (false) if both are true or one (true) if any is true.
 
 @item And
 
+@sethl- olang
 @verbatim
 expr1 && expr2
 @end verbatim
@@ -190,24 +207,28 @@ Results zero (false) if any is false or one (true) if both are true.
 
 @item Shift left
 
+@sethl- olang
 @verbatim
 n << bits
 @end verbatim
 
 @item Shift left
 
+@sethl- olang
 @verbatim
 n >> bits
 @end verbatim
 
 @item And
 
+@sethl- olang
 @verbatim
 n & bits
 @end verbatim
 
 @item Or
 
+@sethl- olang
 @verbatim
 n | bits
 @end verbatim
@@ -220,30 +241,35 @@ n | bits
 
 @item Addition
 
+@sethl- olang
 @verbatim
 expr1 + expr2
 @end verbatim
 
 @item Subtraction
 
+@sethl- olang
 @verbatim
 expr1 - expr2
 @end verbatim
 
 @item Multiplication
 
+@sethl- olang
 @verbatim
 expr1 * expr2
 @end verbatim
 
 @item Division
 
+@sethl- olang
 @verbatim
 expr1 / expr2
 @end verbatim
 
 @item Remaining
 
+@sethl- olang
 @verbatim
 expr1 % expr2
 @end verbatim
@@ -258,6 +284,7 @@ Dealing with memory address.
 
 Get the address of a variable.
 
+@sethl olang
 @verbatim
 var my_var: u32 = 0
 
@@ -268,6 +295,7 @@ var my_pointer: u32* = &my_var
 
 Accessing the value of the address a pointer points to.
 
+@sethl olang
 @verbatim
 var my_var: u32 = 0
 
diff --git a/docs/info/specification.texi b/docs/info/specification.texi
index 707d932..47dad9a 100644
--- a/docs/info/specification.texi
+++ b/docs/info/specification.texi
@@ -1,3 +1,5 @@
+@include _ext.texi
+
 @node Language Specification
 @chapter Specification
 
@@ -19,4 +21,5 @@ This is the O Programming Language EBNF grammar specification[^1]
 NOTE: This grammar spec is a DRAFT and it covers only a small portion of the
 language.
 
+@sethl ebnf
 @verbatiminclude olang.ebnf
diff --git a/docs/style.css b/docs/style.css
index 9ee71e3..0a50254 100644
--- a/docs/style.css
+++ b/docs/style.css
@@ -44,10 +44,6 @@ dd {
   margin-left: 4ch;
 }
 
-pre {
-  margin-bottom: 1rem;
-}
-
 p {
   padding-bottom: 1ch;
 }
@@ -109,12 +105,90 @@ footer {
 }
 
 pre {
-  background: gray;
-  color: black;
-  padding: 2ch;
-  font-weight: bold;
-  max-width: 100%;
+  margin-bottom: 1ch;
+  max-width: calc(100vw - 7ch);
+  width: calc(100vw - 7ch);
   overflow: auto;
+  position: relative;
+  counter-reset: line;
+  background: black;
+  color: white;
+  border-left: 1ch solid grey;
+  padding: 2ch 1ch;
+}
+
+dd pre {
+  max-width: calc(100vw - 11ch);
+  width: calc(100vw - 11ch);
+}
+
+pre:before, pre:after {
+  content: "";
+  background: grey;
+  position: absolute;
+  left: 0ch;
+  width: 1ch;
+  height: 1ch;
+}
+
+pre:before {
+  top: 0ch;
+}
+pre:after {
+  bottom: 0ch;
+}
+
+.code-line {
+  counter-increment: line;
+}
+
+.code-line:before {
+  content: "" counter(line, decimal-leading-zero) " ";
+  color: grey;
+}
+
+.no-numbers .code-line:before {
+  content: ""
+}
+
+.code-key {
+  color: yellow;
+}
+
+.code-fn-name {
+  color: aqua;
+}
+
+.code-symbol,.code-brac {
+  color: silver;
+}
+
+.code-num {
+  color: orange;
+}
+
+.code-type {
+  color: lime;
+}
+
+.code-comment {
+  color: grey;
+}
+
+.code-macro {
+  color: orange;
+}
+
+.code-str {
+  color: lime;
+}
+
+.lang-output {
+  color: grey;
+}
+
+.lang-bash .code-opt {
+  color: silver;
 }
 
 .logo-name {
-- 
2.46.1


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang] docs: layout: change website look and feel
@ 2024-11-06 11:42 Carlos Maniero
  2024-11-06 11:43 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-11-06 11:42 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

This changes is based considering the follow:

- olang is a simple language so must be its website
  - changes:
    - All colors used are basic colors [1]
    - All measure units are ch(ars)
      - We were already mostly using this but there was some default
        browser behavior that was using px, the browser's defaults was
        reset.
- info html navbar bring a lot of distraction.
  - most of the info pages we have the navbar occupies two lines.
    - the "[Contents]" used to be alone in the second line (orphan)
      making the website to look awful.
  - changes:
    - website is no longer centralized
      - the content is still limited in 80ch but the navbar is free to
        grow.
      - this make the info pages to look a lot more like an info page
        and the navigation is way more understandable.

[1]: https://www.w3.org/wiki/CSS/Properties/color/keywords

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 docs/style.css | 102 +++++++++++++++++++++++++++++++++----------------
 1 file changed, 70 insertions(+), 32 deletions(-)

diff --git a/docs/style.css b/docs/style.css
index d6cf74a..9ee71e3 100644
--- a/docs/style.css
+++ b/docs/style.css
@@ -1,19 +1,57 @@
 :root {
-  --background-color: #f4f4f4;
+  --background-color: white;
   --text-color: black;
-  --link-color: #4e5289;
-  --text-highlight-color: rgba(1, 1, 1, 0.1);
+  --link-color: teal;
+  --text-highlight-color: silver;
 }
 
 @media (prefers-color-scheme: dark) {
   :root {
-    --background-color: #26282B;
+    --background-color: black;
     --text-color: white;
-    --link-color: #8c91db;
-    --text-highlight-color: rgba(255, 255, 255, 0.1);
+    --link-color: aqua;
+    --text-highlight-color: grey;
   }
 }
 
+* {
+  margin: 0;
+  padding: 0;
+}
+
+a {
+  color: var(--link-color);
+}
+
+h1, h2, h3, h4, h5, h6 {
+  font-size: 1rem;
+  margin: 0;
+  margin-bottom: 1ch;
+}
+
+ul {
+  margin: 0 4ch 2ch;
+}
+
+ul ul {
+  margin-bottom: 0;
+}
+
+dt {
+  margin-bottom: 1ch;
+}
+dd {
+  margin-left: 4ch;
+}
+
+pre {
+  margin-bottom: 1rem;
+}
+
+p {
+  padding-bottom: 1ch;
+}
+
 div.example {
   margin: 0;
 }
@@ -22,7 +60,7 @@ body {
   background: var(--background-color);
   color: var(--text-color);
   margin: 0;
-  padding: 0 40px;
+  padding: 0 2ch 2ch;
   font-size: 1rem;
   font-family: monospace;
 }
@@ -40,28 +78,24 @@ body {
   text-align: left !important;
 }
 
-a {
-  color: var(--link-color);
-}
-
-h1, h2, h3, h4, h5, h6 {
-  font-size: 1em;
-  margin: 0;
-  margin-bottom: 1ch;
+header {
+  margin: 2ch auto;
+  border-bottom: 1ch solid var(--text-color);
+  padding-bottom: 2ch;
 }
 
-body > * {
-  max-width: 80ch;
-  margin: 0 auto;
+header > h1 {
+  font-weight: normal;
+  margin-bottom: 2ch;
 }
 
-header {
-  margin: 6ch auto;
+.nav-panel * {
+  margin: 0;
+  padding: 0;
 }
 
-header > h1 {
-  font-weight: normal;
-  font-size: 1em;
+article, body > div:not(.nav-panel) > *:not(.nav-panel):not(hr) {
+  max-width: 80ch;
 }
 
 header > nav > span {
@@ -69,30 +103,34 @@ header > nav > span {
 }
 
 footer {
-  margin: 4ch auto;
+  margin: 2ch auto;
+  padding-top: 2ch;
+  border-top: 1ch solid var(--text-color);
 }
 
 pre {
-  background: rgba(0,0,0, 0.75);
-  color: white;
-  padding: 1.5em;
+  background: gray;
+  color: black;
+  padding: 2ch;
+  font-weight: bold;
   max-width: 100%;
   overflow: auto;
-  font-size: large;
-  line-height: 1;
 }
 
 .logo-name {
   font-weight: bold;
+  background: var(--link-color);
+  padding: 0 1ch;
+  color: var(--background-color);
 }
 
 hr {
   border: none;
-  height: 1px;
-  opacity: 0.5;
+  height: 1ch;
   background-color: var(--text-color);
+  margin: 2ch 0;
 }
 
 ol {
-  margin-left: 1.5em;
+  margin-left: 2ch;
 }

base-commit: 9800d80ac1451e48d946d1d6bc94ff33536b0bdf
-- 
2.46.1


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1 2/2] docs: add the C FFI section
@ 2024-11-06  4:11 Carlos Maniero
  2024-11-06  4:12 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-11-06  4:11 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

This section describe both: How to call O lang from C and how to call C
from O.

Even that the "Calling O from C" may change as we evolve the discussions
if we you use or not some symbol mangle strategy it is worth to
document what we have working so far.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 docs/info/c-ffi.texi | 102 +++++++++++++++++++++++++++++++++++++++++++
 docs/info/olang.texi |   3 ++
 2 files changed, 105 insertions(+)
 create mode 100644 docs/info/c-ffi.texi

diff --git a/docs/info/c-ffi.texi b/docs/info/c-ffi.texi
new file mode 100644
index 0000000..719fcd5
--- /dev/null
+++ b/docs/info/c-ffi.texi
@@ -0,0 +1,102 @@
+@node C FFI
+@chapter C FFI
+
+O programming language follows C's calling conventions, meaning that every O
+function can be called by C without any extra effort.
+
+All example bellow will use @code{gcc} but you can use the compiler/linker of
+your preference.
+
+@section Calling C from O
+
+To call external code you can use the @code{extern} statement.
+
+@subsection extern
+
+@verbatim
+# file: answer.ol
+
+extern fn u32print(number: u32): u32
+
+fn main(): u8 {
+  u32print(42)
+
+  return 0
+}
+@end verbatim
+
+That way instead of defining the @code{u32print} function. It must be provided
+at the linkage step.
+
+@verbatim
+// file: u32print.c
+
+#include <stdio.h>
+
+int u32print(int number) {
+  return printf("%d\n", number);
+}
+@end verbatim
+
+@subsection Compiling without linking
+
+If you try to compile the answer.ol file you will receive the follow error:
+
+@verbatim
+olc answer.ol -o answer.o
+/usr/bin/ld: answer.o.o: in function `main':
+(.text+0x10): undefined reference to `u32print'
+collect2: error: ld returned 1 exit status
+@end verbatim
+
+That's because @code{O} tries to link by default. To assemble the code without
+linking it, you can use the @code{-c} option.
+
+@verbatim
+olc answer.ol -c -o answer.o
+@end verbatim
+
+After that you can do the same with the C file that contains the function that
+you are looking to call.
+
+@verbatim
+gcc u32print.c -c -o u32print.o
+@end verbatim
+
+And then you can link both object files into an executable:
+
+@verbatim
+gcc answer.o u32print.o -o answer
+@end verbatim
+
+@subsection Calling O from C
+
+All that is required is to set the function prototype.
+
+@verbatim
+# file: sum.ol
+
+fn sum(a: u32, b: u32): u32 {
+  return a + b
+}
+@end verbatim
+
+@verbatim
+# file: csum.c
+
+#include <stdio.h>
+
+int sum(int a, int b);
+
+int main() {
+  printf("%d\n", sum(41, 1));
+  return 0;
+}
+@end verbatim
+
+Compiling:
+
+@verbatim
+olc sum.ol -c -o sum.o
+gcc sum.o csum.c -o csum
+@end verbatim
diff --git a/docs/info/olang.texi b/docs/info/olang.texi
index eea4c29..e6339c1 100644
--- a/docs/info/olang.texi
+++ b/docs/info/olang.texi
@@ -27,6 +27,7 @@ programming language.
 * Introduction::
 * Installation::
 * Getting Started::
+* C FFI::
 * Language Specification::
 * Contribution Guide::
 @end menu
@@ -37,6 +38,8 @@ programming language.
 
 @include getting-started.texi
 
+@include c-ffi.texi
+
 @include specification.texi
 
 @include contribution-guide.texi
-- 
2.46.1


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v2 6/6] ast: remove dead code from var_assign ast node
@ 2024-10-31  3:13 Carlos Maniero
  2024-10-31  3:14 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-31  3:13 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

Previously, the var_assign was a ast_node but it was replaced to be an
binop. The previous struct was not removed tho.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 src/ast.h | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/src/ast.h b/src/ast.h
index 316f921..6ca70e9 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -182,13 +182,6 @@ typedef struct ast_unary_op
     ast_node_t *operand;
 } ast_unary_op_t;
 
-typedef struct ast_var_assign_stmt
-{
-    AST_NODE_HEAD;
-    ast_node_t *ref;
-    ast_node_t *value;
-} ast_var_assign_stmt_t;
-
 typedef struct ast_return_stmt
 {
     AST_NODE_HEAD;
@@ -222,7 +215,6 @@ typedef union ast_node
     ast_literal_t as_literal;
     ast_ref_t as_ref;
     ast_block_t as_block;
-    ast_var_assign_stmt_t as_var_assign_stmt;
     ast_return_stmt_t as_return_stmt;
     ast_if_stmt_t as_if_stmt;
     ast_while_stmt_t as_while_stmt;
-- 
2.46.1


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang] docs: add script to generate man pages from docstring
@ 2024-10-28  2:06 Carlos Maniero
  2024-10-28  2:07 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-28  2:06 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

There is no initial intention to make this manuals public but just to
support the core developers.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
The Makefile was inspired on the test/olc/Makefile and is supporting
parallelism.

If you have manpath in your system you can use the following
configuration:

echo "MANDATORY_MANPATH /the_location_to_/olang/docs/man/" >> ~/.manpath

 Makefile              |   7 +
 scripts/gen-docstring | 704 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 711 insertions(+)
 create mode 100755 scripts/gen-docstring

diff --git a/Makefile b/Makefile
index 58526ac..2d021d2 100644
--- a/Makefile
+++ b/Makefile
@@ -20,6 +20,7 @@ BUILDDIR := build
 
 SRCS := $(wildcard $(SRCDIR)/*.c)
 HEADERS := $(wildcard $(SRCDIR)/*.h)
+DEV_MAN_PAGES := $(patsubst %, %.man, $(HEADERS))
 OBJS := $(patsubst $(SRCDIR)/%.c, $(BUILDDIR)/%.o, $(SRCS))
 
 .PHONY: all
@@ -115,6 +116,12 @@ docs:
 docs-dist:
 	$(MAKE) -C docs dist
 
+.PHONY:
+dev-man-docs: $(DEV_MAN_PAGES)
+
 $(BUILDDIR)/%.o: $(SRCDIR)/%.c
 	@$(CC) $(CFLAGS) -c $< -o $@
 	@printf 'CC\t%s\n' '$@'
+
+$(SRCDIR)/%.man: $(SRCDIR)/%
+	@./scripts/gen-docstring $< `basename $<` docs/man/
diff --git a/scripts/gen-docstring b/scripts/gen-docstring
new file mode 100755
index 0000000..ec3afcb
--- /dev/null
+++ b/scripts/gen-docstring
@@ -0,0 +1,704 @@
+#!/bin/python3
+
+# Copyright (C) 2024 Carlos Maniero<carlos@maniero.me>
+# Copyright (C) 2024 Johnny Richard<johnny@johnnyrichard.com>
+#
+# 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/>.
+
+# TODO: Create a man for the text bellow:
+"""
+gen-docstring - Parses docstring into man.
+
+USAGE:
+    ./scripts/gen-docstring file_name import_name man_output_dir
+
+RESTRICTIONS:
+
+    - It works for macros definition, typedef struct, typedef union, functions
+    - It may not work with definitions that uses macros
+    - It does not works well with inner structs/union
+
+SUPPORTED FEATURES:
+
+    BASIC DOCSTRING:
+
+        /**
+         * This is the man page title
+         */
+        typedef struct my_struct
+        {
+        } my_struct_t;
+
+        This will generate a man page for the my_struct_t.
+
+    SECTIONS:
+
+        All sections must start with a capital letter and must include a dash
+        line above.
+
+        /**
+         * This is the man page title
+         *
+         * This is my description
+         * .B it supports groff syntax
+         *
+         * This is my section
+         * ------------------
+         *
+         * This is my description
+         * .B it supports groff syntax
+         */
+
+         All the content disposed right after the man page belongs to the
+         description section if no section is defined.
+
+    GROUPS:
+
+        You may want to group many docstrings into a single man page. So you
+        can use group_name(3) where 3 is the man level.
+
+        /**
+         * my_group(3) - This is the man page title
+         *
+         * Description of the first item
+         */
+        ...
+
+        /**
+         * my_group(3) - This text will be ignored
+         *
+         * Description of the second item
+         */
+        ...
+
+        The script will generate a single file within the group_name the other
+        declarations will be linked to the file within the group_name.
+
+        The title of the declaration that matches the structure been commented
+        will be used as the man page title.
+"""
+
+import os
+import sys
+import re
+from enum import Enum
+from collections import namedtuple
+
+Ctx = namedtuple('Ctx', 'filename include_name man_output_dir')
+
+ctx = None
+
+Token = namedtuple('Token', 'kind value location')
+
+token_rules = [
+    docstr_rule    := r'(?P<DOCSTR>/\*\*.*?\*/)',
+    bcomment_rule  := r'(?P<BDOCSTR>/\*.*?\*/)',
+    comment_rule   := r'(?P<COMMENT>//[^\n]*)',
+    keyword_rule   := r'(?P<KEYWORD>typedef|struct|union|enum)[\s]',
+    marco_rule     := r'(?P<MACRO>#[a-zA-Z_][a-zA-Z0-9_]*)',
+    string_rule    := r'(?P<STRING>"([^"\\]*(\\.[^"\\])*)*")',
+    hex_rule       := r'(?P<HEX>0[xX][0-9a-fA-F]+)',
+    octal_rule     := r'(?P<OCTAL>0[oO]?[0-8]+)',
+    binary_rule    := r'(?P<BINARY>0[bB]?[0-1]+)',
+    decimal_rule   := r'(?P<DECIMAL>\d+)',
+    char_rule      := r'(?P<CHAR>\'[\w ]\')',
+    comma_rule     := r'(?P<COMMA>,)',
+    dot_rule       := r'(?P<DOT>\.)',
+    arrow_rule     := r'(?P<ARROW>->)',
+    lshift_rule    := r'(?P<LSHIFT><<)',
+    rshift_rule    := r'(?P<RSHIFT>>>)',
+    semicolon_rule := r'(?P<SEMICOLON>;)',
+    eq_rule        := r'(?P<EQ>=)',
+    star_rule      := r'(?P<STAR>\*)',
+    plus_rule      := r'(?P<PLUS>\+)',
+    slash_rule     := r'(?P<SLASH>/)',
+    and_rule       := r'(?P<AND>&)',
+    pipe_rule      := r'(?P<PIPE>\|)',
+    tilde_rule     := r'(?P<TILDE>~)',
+    bang_rule      := r'(?P<BANG>!)',
+    dash_rule      := r'(?P<DASH>-)',
+    lbrace_rule    := r'(?P<LBRACE>{)',
+    rbrace_rule    := r'(?P<RBRACE>})',
+    lparen_rule    := r'(?P<LPAREN>\()',
+    rparen_rule    := r'(?P<RPAREN>\))',
+    ident_rule     := r'(?P<IDENT>[a-zA-Z_][a-z-A-Z0-9_]*)',
+    ws_rule        := r'(?P<WS>\s+)',
+]
+
+tokenizer_pattern = re.compile('|'.join(token_rules), re.DOTALL)
+
+def tokenize(code):
+    pos = 0
+    tokens = []
+    while pos < len(code):
+        match = tokenizer_pattern.match(code, pos)
+        if match:
+            token_kind = match.lastgroup
+
+            if token_kind != 'WS':
+                tokens.append(Token(token_kind, match.group(token_kind), match.span()))
+            pos = match.end()
+        else:
+            tokens.append(Token('UNKNOWN', code[pos], (pos, pos + 1)))
+            pos += 1
+    return tokens
+
+
+re_section_name = r'[A-Z].*$'
+re_group = r'(([a-zA-Z_]\w+)\((.*?)\))'
+re_group_title = r'^(?P<group_name>([a-zA-Z_]\w+))\((?P<man_level>\d)\)( - (?P<title>.*))?'
+
+
+Field = namedtuple('Field', 'name type')
+EnumField = namedtuple('EnumField', 'name value')
+
+
+class Section:
+    def __init__(self, name, contents):
+        self.name = name
+        self.contents = contents
+        self.subsections = []
+
+
+NodeKind = Enum('NodeKind', ['FUNCTION', 'TYPEDEF_STRUCT', 'TYPEDEF_UNION', 'TYPEDEF_ENUM', 'MACRO'])
+
+
+class DocString:
+    def __init__(self, comment_lines, code_node):
+        self.comment_lines = comment_lines
+        self.code_node = code_node
+
+    def get_name(self):
+        return self.code_node.get_name()
+
+    def get_group_name(self):
+        if self.is_group():
+            return re.match(re_group_title, self.comment_lines[0]).group('group_name')
+        return self.get_name()
+
+    def is_group(self):
+        return re.match(re_group_title, self.comment_lines[0])
+
+    def get_man_level(self):
+        if self.is_group():
+            return re.match(re_group_title, self.comment_lines[0]).group('man_level')
+        return 3
+
+    def is_entry_doc(self):
+        return self.get_group_name() == self.get_name()
+
+    def get_title(self):
+        if self.is_group():
+            return re.match(re_group_title, self.comment_lines[0]).group('title')
+        return self.comment_lines[0]
+
+    def get_description(self):
+        description = ""
+        for line in self.comment_lines[2:]:
+            if line.startswith('='):
+                break
+            elif len(description) > 0 and description[-1] != '\n':
+                description += ' '
+            description += line + '\n'
+        return description.strip()
+
+    def get_sections(self):
+        sections = []
+
+        section_name = None
+        section_contents = ""
+
+        lines = self.comment_lines[2:]
+        index = 0
+
+        while len(lines) > index:
+            line = lines[index]
+            next_line = None
+
+            if len(lines) > index + 1:
+                next_line = lines[index + 1]
+
+            if re.match(re_section_name, line) and (next_line and re.match('-+', next_line)):
+                if section_name:
+                    sections.append(Section(section_name, section_contents[1:-1]))
+                    section_name = None
+                    section_contents = ""
+                section_name = line.strip().upper()
+                index += 1
+            elif index == 0:
+                section_name = 'DESCRIPTION'
+                section_contents += line + '\n'
+            elif section_name:
+                section_contents += line + '\n'
+
+            index += 1
+
+        if section_name:
+            sections.append(Section(section_name, section_contents))
+
+        return sections
+
+
+class DocStringFile:
+    def __init__(self, docstrings):
+        self.docstrings = docstrings
+
+    def get_name(self):
+        return self.docstrings[0].get_group_name()
+
+    def get_man_level(self):
+        return self.docstrings[0].get_man_level()
+
+    def get_group_name(self):
+        names = []
+
+        for docstring in self.docstrings:
+            names.append(docstring.get_name())
+        return ", ".join(names)
+
+    def is_group(self):
+        return len(self.docstrings) > 1
+
+    def get_title(self):
+        return self.docstrings[0].get_title()
+
+    def get_description(self):
+        return self.docstrings[0].get_description()
+
+    def get_filepath(self):
+        man_level = self.get_man_level()
+        filename = f'{self.get_name()}.{man_level}'
+        return os.path.join(ctx.man_output_dir, f'man{man_level}', filename)
+
+    def get_links(self):
+        man_level = self.get_man_level()
+        to = os.path.join(f'man{man_level}', f'{self.get_name()}.{man_level}')
+
+        for docstring in self.docstrings:
+            if docstring.is_entry_doc():
+                continue
+            filename = f'{docstring.get_name()}.{man_level}'
+            yield (os.path.join(ctx.man_output_dir, f'man{man_level}', filename), to)
+
+    def get_sections(self):
+        if not self.is_group():
+            return self.docstrings[0].get_sections()
+
+        sections = []
+
+        for docstring in self.docstrings:
+            for section in docstring.get_sections():
+                parent_section = next(filter(lambda x: x.name == section.name, sections), None)
+
+                if not parent_section:
+                    parent_section = Section(section.name, '')
+                    sections.append(parent_section)
+
+                parent_section.subsections.append(section)
+                section.name = docstring.get_name()
+
+        return sections
+
+
+class MacroNode:
+    def __init__(self, tokens, file_contents):
+        self.tokens = tokens
+        self.kind = NodeKind.MACRO
+        self.file_contents = file_contents
+
+    def get_name(self):
+        return self.tokens[1].value
+
+    def get_contents(self):
+        start_location = self.tokens[0].location[0]
+
+        index = 0;
+
+        while True:
+            cur_char = self.file_contents[start_location + index]
+            next_char = self.file_contents[start_location + index + 1]
+
+            if next_char == '\n' and cur_char != '\\':
+                break
+
+            index += 1
+
+        return self.file_contents[start_location:start_location+index+1]
+
+
+class TypedefNode:
+    def __init__(self, tokens, kind):
+        self.tokens = tokens
+        self.kind = kind
+
+    def get_name(self):
+        tokens = self.tokens
+
+        # FIXME: support typdef without brackets
+        while len(tokens) > 0 and tokens[0].value != '{':
+            tokens = tokens[1:]
+        else:
+            tokens = tokens[1:]
+
+        open_brackets = 1
+
+        while len(tokens) > 0 and open_brackets != 0:
+            if tokens[0].value == '{':
+                open_brackets += 1
+            elif tokens[0].value == '}':
+                open_brackets -= 1
+            tokens = tokens[1:]
+
+        if len(tokens) == 0:
+            raise Exception("could not find the typedef name")
+
+        return tokens[0].value
+
+    def get_declaration(self):
+        final_index = 0
+
+        tokens = self.tokens
+        while tokens[0].value != '{':
+            tokens = tokens[1:]
+            final_index += 1
+
+        return " ".join([token.value for token in self.tokens[0:final_index]])
+
+    def get_fields(self):
+        tokens = self.tokens
+        while tokens[0].value != '{':
+            tokens = tokens[1:]
+        else:
+            tokens = tokens[1:]
+
+        while tokens[0].value != '}':
+            #FIXME: support inner declarations properly
+            #       It is working but with a poor representation
+            end_index = 1
+
+            level = 0
+            while tokens[end_index].value != ';' or level > 0:
+                if tokens[end_index].value == '{':
+                    level += 1
+                elif tokens[end_index].value == '}':
+                    level -= 1
+                end_index += 1
+
+            token_name = tokens[end_index - 1].value
+            token_type = " ".join([token.value for token in tokens[0:end_index - 1]])
+
+            yield Field(token_name, token_type)
+
+            tokens = tokens[end_index + 1:]
+
+
+class TypedefEnumNode(TypedefNode):
+    def tokens_to_field(self, value_tokens):
+        if len(value_tokens) > 2:
+            return EnumField(value_tokens[0], " ".join(value_tokens[2:]))
+        return EnumField(value_tokens[0], None)
+
+    def get_fields(self):
+        tokens = self.tokens
+        while tokens[0].value != '{':
+            tokens = tokens[1:]
+        else:
+            tokens = tokens[1:]
+
+        value_tokens = []
+
+        while True:
+            if tokens[0].value == ',' or tokens[0] == '}':
+                yield self.tokens_to_field(value_tokens)
+                value_tokens = []
+            else:
+                value_tokens.append(tokens[0].value)
+
+            if tokens[0].value == '}':
+                break
+
+            tokens = tokens[1:]
+        else:
+            if len(value_tokens) > 0:
+                yield self.tokens_to_field(value_tokens)
+
+
+
+class FunctionNode:
+    def __init__(self, tokens):
+        self.tokens = tokens
+        self.kind = NodeKind.FUNCTION
+
+    def get_ret_type(self):
+        end_index = 0
+
+        while self.tokens[end_index].value != '(':
+            end_index += 1
+
+        return " ".join([token.value for token in self.tokens[0:end_index - 1]])
+
+    def get_name(self):
+        tokens = self.tokens
+
+        while len(tokens) > 1 and tokens[1].value != '(':
+            tokens = tokens[1:]
+
+        return tokens[0].value
+
+    def get_fields(self):
+        tokens = self.tokens
+        while tokens[0].value != '(':
+            tokens = tokens[1:]
+        else:
+            tokens = tokens[1:]
+
+        while True:
+            end_index = 1
+
+            while tokens[end_index].value != ',' and tokens[end_index].value != ')':
+                end_index += 1
+
+
+            token_name = tokens[end_index - 1].value
+
+            # FIXME: use file file contents to get this range
+            token_type = " ".join([token.value for token in tokens[0:end_index - 1]])
+
+            yield Field(token_name, token_type)
+
+            if tokens[end_index].value == ')':
+                break
+
+            tokens = tokens[end_index + 1:]
+
+
+def get_code_node(tokens, file):
+    if tokens[0].kind == 'MACRO':
+        return MacroNode(tokens, file)
+    # FIXME: allows structures without typedef
+    if tokens[0].value == 'typedef' and tokens[1].value == 'union':
+        return TypedefNode(tokens, NodeKind.TYPEDEF_UNION)
+    if tokens[0].value == 'typedef' and tokens[1].value == 'struct':
+        return TypedefNode(tokens, NodeKind.TYPEDEF_STRUCT)
+    if tokens[0].value == 'typedef' and tokens[1].value == 'enum':
+        return TypedefEnumNode(tokens, NodeKind.TYPEDEF_ENUM)
+    return FunctionNode(tokens)
+
+
+def group_docstring_into_files(docstrings):
+    files = []
+
+    docstrings = sorted(docstrings, key=lambda x: x.is_entry_doc(), reverse=True)
+
+    for docstring in docstrings:
+        file = next(filter(lambda x: x.get_name() == docstring.get_group_name(), files), None)
+
+        if docstring.is_group() and file:
+            file.docstrings.append(docstring)
+        else:
+            files.append(DocStringFile([docstring]))
+
+    return files
+
+
+def extract_comment(comment):
+    lines = comment.splitlines()
+
+    comment_lines = []
+
+    for line in lines[1:]:
+        if line.strip().startswith("*/"):
+            break
+
+        comment_lines.append(line.strip()[2:])
+
+    return comment_lines
+
+
+def extract_docstring(tokens, lines):
+    docstrings = []
+
+    while len(tokens) > 0:
+        token = tokens[0]
+        if token.kind == 'DOCSTR':
+            docstrings.append(
+                DocString(
+                    extract_comment(token.value),
+                    get_code_node(tokens[1:], lines)
+                )
+            )
+
+        tokens = tokens[1:]
+
+    return group_docstring_into_files(docstrings)
+
+def man_print_fn_synopsis(docstring):
+    groff_lines = []
+
+    groff_lines.append(f".nf")
+    fields = list(docstring.code_node.get_fields())
+    paren = f"{docstring.code_node.get_ret_type()} {docstring.code_node.get_name()}("
+    paren_len = len(paren)
+    post = ","
+    for index, field in enumerate(fields):
+        if index == len(fields) - 1:
+            post = ");"
+
+        groff_lines.append(f".BI \"{paren}{field.type} \" {field.name} {post}")
+
+        paren = " " * paren_len
+
+    groff_lines.append(f".fi")
+    return groff_lines
+
+
+def man_print_typedef(docstring):
+    groff_lines = []
+    groff_lines.append(f'.B "{docstring.code_node.get_declaration()}"')
+    groff_lines.append('.br')
+    groff_lines.append('.B "{"')
+
+    for index, field in enumerate(docstring.code_node.get_fields()):
+        groff_lines.append('.br')
+        if field.type:
+            groff_lines.append(f'.BI "    {field.type} " "{field.name}";')
+        else:
+            groff_lines.append(f'.B "    {field.name};"')
+
+    groff_lines.append('.br')
+    groff_lines.append(f'.B "}} {docstring.code_node.get_name()};"')
+    return groff_lines
+
+
+def man_print_enum_synopisis(docstring):
+    groff_lines = []
+    groff_lines.append(f'.B "{docstring.code_node.get_declaration()}"')
+    groff_lines.append('.br')
+    groff_lines.append('.B "{"')
+
+    for index, field in enumerate(docstring.code_node.get_fields()):
+        groff_lines.append('.br')
+        if field.value:
+            groff_lines.append(f'.BR "    {field.name} " "= {field.value}",')
+        else:
+            groff_lines.append(f'.B "    {field.name},"')
+
+    groff_lines.append('.br')
+    groff_lines.append(f'.B "}} {docstring.code_node.get_name()};"')
+
+    return groff_lines
+
+def man_print_macro(docstring):
+    contents = docstring.code_node.get_contents().replace('\\', '\\\\').splitlines()
+    return [
+        '.nl'
+        '.B' + contents[0],
+        ] + contents[1:] + ['.ni']
+
+
+def ascii_to_groff(text):
+    text = re.sub(r'^ *<code>', '.EX', text, flags=re.M)
+    text = re.sub(r'^ *</code>', '.EE', text, flags=re.M)
+    text = re.sub(r'<b>(.*?)</b>', r'\\fB\1\\fR', text, flags=re.M)
+    text = re.sub(re_group, r'\\fB\2\\fR(\3)', text, flags=re.M)
+    text = re.sub(r'<i>(.*?)</i>', r'\\fI\1\\fR', text, flags=re.M)
+
+    groff_lines = []
+    ascii_lines = text.splitlines()
+
+    line_index = 0
+
+    while len(ascii_lines) > line_index:
+        ascii_line = ascii_lines[line_index]
+
+        if ascii_line.startswith('@'):
+            groff_lines.append('.TP')
+
+            field_name = ascii_line.split(':')[0][1:]
+            groff_lines.append(f'.I {field_name}')
+
+            description = ":".join(ascii_line.split(':')[1:]).strip()
+
+            while len(ascii_lines) > line_index + 1 and ascii_lines[line_index + 1][0] == ' ':
+                description += ' ' + ascii_lines[line_index + 1].strip()
+                line_index += 1
+            groff_lines.append(description)
+
+        else:
+            groff_lines.append(ascii_line)
+
+        line_index += 1
+
+    return groff_lines
+
+
+def generate_docs():
+    with open(ctx.filename) as file:
+        file_contents = file.read()
+
+        for doc_file in extract_docstring(tokenize(file_contents), file_contents):
+            groff_lines = []
+
+            groff_lines.append(f".TH {doc_file.get_name()} {doc_file.get_man_level()} {doc_file.get_name()} \"\" \"Olang Hacker's manual\"")
+            groff_lines.append(f".SH NAME")
+            groff_lines.append(f"{doc_file.get_group_name()} \\- {doc_file.get_title()}")
+
+            groff_lines.append(f".SH SYNOPSIS")
+
+            groff_lines.append(f".B #include <{ctx.include_name}>")
+            groff_lines.append(f".P")
+
+            for index, docstring in enumerate(doc_file.docstrings):
+                if index:
+                    groff_lines.append('.P')
+                node = docstring.code_node
+                if node.kind == NodeKind.FUNCTION:
+                    groff_lines += man_print_fn_synopsis(docstring)
+                elif node.kind in [NodeKind.TYPEDEF_STRUCT, NodeKind.TYPEDEF_UNION]:
+                    groff_lines += man_print_typedef(docstring)
+                elif node.kind == NodeKind.MACRO:
+                    groff_lines += man_print_macro(docstring)
+                elif node.kind == NodeKind.TYPEDEF_ENUM:
+                    groff_lines += man_print_enum_synopisis(docstring)
+
+            for section in doc_file.get_sections():
+                groff_lines.append(f'.SH {section.name}')
+                groff_lines += ascii_to_groff(section.contents)
+
+                for subsection in section.subsections:
+                    groff_lines.append(f'.SS {subsection.name}')
+                    groff_lines += ascii_to_groff(subsection.contents)
+
+            print(f'MAN\t{doc_file.get_filepath()}')
+            os.makedirs(os.path.dirname(doc_file.get_filepath()), exist_ok=True)
+            file = open(doc_file.get_filepath(), "w")
+            file.write("\n".join(groff_lines))
+            file.close()
+
+            for (link, to) in doc_file.get_links():
+                print(f'MAN\t{link}')
+                os.makedirs(os.path.dirname(link), exist_ok=True)
+                file = open(link, "w")
+                file.write(f".so {to}")
+                file.close()
+
+
+if __name__ == "__main__":
+    if len(sys.argv) != 4:
+        print(f'USAGE:\n\t{sys.argv[0]} file include_name man_output_dir')
+        exit(1)
+
+    ctx = Ctx(*sys.argv[1:])
+
+    generate_docs()

base-commit: f87fb371a0105a458be07bd3f269bb45da913d16
-- 
2.46.1


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1 6/6] ast: remove dead code from var_assign ast node
@ 2024-10-24 12:38 Carlos Maniero
  2024-10-24 12:39 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-24 12:38 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

Previously, the var_assign was a ast_node but it was replaced to be an
binop. The previous struct was not removed tho.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 src/ast.h | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/src/ast.h b/src/ast.h
index 8cf072a..4ba0848 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -182,13 +182,6 @@ typedef struct ast_unary_op
     ast_node_t *operand;
 } ast_unary_op_t;
 
-typedef struct ast_var_assign_stmt
-{
-    AST_NODE_HEAD;
-    ast_node_t *ref;
-    ast_node_t *value;
-} ast_var_assign_stmt_t;
-
 typedef struct ast_return_stmt
 {
     AST_NODE_HEAD;
@@ -222,7 +215,6 @@ typedef union ast_node
     ast_literal_t as_literal;
     ast_ref_t as_ref;
     ast_block_t as_block;
-    ast_var_assign_stmt_t as_var_assign_stmt;
     ast_return_stmt_t as_return_stmt;
     ast_if_stmt_t as_if_stmt;
     ast_while_stmt_t as_while_stmt;
-- 
2.46.1


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1 5/5] semantics: add scope to all nodes
@ 2024-10-23  2:20 Carlos Maniero
  2024-10-23  2:21 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-23  2:20 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

Instead of manually adding scopes to nodes as needed, now the scope is a
base attribute. This makes the implementation simpler and consistent so
we no longer need to think if some node kind requires or not the scope.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 src/ast.h            |  5 +----
 src/codegen_x86_64.c | 15 ++++++++-------
 src/type_checker.c   | 12 +++++-------
 3 files changed, 14 insertions(+), 18 deletions(-)

diff --git a/src/ast.h b/src/ast.h
index 7c5e9af..abc12e2 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -30,6 +30,7 @@
     {                                                                          \
         ast_node_kind_t kind;                                                  \
         token_loc_t loc;                                                       \
+        scope_t *scope;                                                        \
     }
 
 /**
@@ -88,7 +89,6 @@ typedef struct ast_fn_definition
     type_t *return_type;
     bool _extern;
     ast_node_t *block;
-    scope_t *scope;
 } ast_fn_definition_t;
 
 typedef struct ast_fn_call
@@ -96,7 +96,6 @@ typedef struct ast_fn_call
     AST_NODE_HEAD;
     string_view_t id;
     list_t *args;
-    scope_t *scope;
 } ast_fn_call_t;
 
 typedef struct ast_var_definition
@@ -105,7 +104,6 @@ typedef struct ast_var_definition
     string_view_t id;
     type_t *type;
     ast_node_t *value;
-    scope_t *scope;
 } ast_var_definition_t;
 
 typedef enum
@@ -127,7 +125,6 @@ typedef struct ast_ref
 {
     AST_NODE_HEAD;
     string_view_t id;
-    scope_t *scope;
 } ast_ref_t;
 
 typedef enum ast_binary_op_kind
diff --git a/src/codegen_x86_64.c b/src/codegen_x86_64.c
index 9006250..616fa2b 100644
--- a/src/codegen_x86_64.c
+++ b/src/codegen_x86_64.c
@@ -143,7 +143,7 @@ codegen_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr_node)
         case AST_NODE_REF: {
             ast_ref_t ref = expr_node->as_ref;
 
-            symbol_t *symbol = scope_lookup(ref.scope, ref.id);
+            symbol_t *symbol = scope_lookup(expr_node->scope, ref.id);
             assert(symbol);
 
             size_t offset = codegen_x86_64_get_stack_offset(codegen, symbol);
@@ -159,7 +159,7 @@ codegen_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr_node)
         case AST_NODE_FN_CALL: {
             ast_fn_call_t fn_call = expr_node->as_fn_call;
 
-            symbol_t *symbol = scope_lookup(fn_call.scope, fn_call.id);
+            symbol_t *symbol = scope_lookup(expr_node->scope, fn_call.id);
             assert(symbol);
 
             size_t i = 0;
@@ -597,7 +597,7 @@ codegen_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr_node)
                     switch (bin_op.lhs->kind) {
                         case AST_NODE_REF: {
                             ast_ref_t ref = bin_op.lhs->as_ref;
-                            scope_t *scope = ref.scope;
+                            scope_t *scope = bin_op.lhs->scope;
 
                             symbol_t *symbol = scope_lookup(scope, ref.id);
                             assert(symbol);
@@ -668,7 +668,8 @@ codegen_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr_node)
 
                     ast_ref_t ref = unary_op.operand->as_ref;
 
-                    symbol_t *symbol = scope_lookup(ref.scope, ref.id);
+                    symbol_t *symbol =
+                        scope_lookup(unary_op.operand->scope, ref.id);
                     assert(symbol);
 
                     size_t offset =
@@ -722,7 +723,7 @@ codegen_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
 
             case AST_NODE_VAR_DEF: {
                 ast_var_definition_t var_def = node->as_var_def;
-                scope_t *scope = var_def.scope;
+                scope_t *scope = node->scope;
 
                 symbol_t *symbol = scope_lookup(scope, var_def.id);
                 assert(symbol);
@@ -908,7 +909,7 @@ codegen_x86_64_emit_function(codegen_x86_64_t *codegen,
 
         ast_fn_param_t *param = item->value;
 
-        symbol_t *symbol = scope_lookup(fn_def->scope, param->id);
+        symbol_t *symbol = scope_lookup(fn_def->base.scope, param->id);
         assert(symbol);
 
         // FIXME: add offset according to the param size
@@ -926,7 +927,7 @@ codegen_x86_64_emit_function(codegen_x86_64_t *codegen,
         ++i;
     }
 
-    size_t local_size = calculate_fn_local_size(fn_def->scope);
+    size_t local_size = calculate_fn_local_size(fn_def->base.scope);
 
     if (local_size != 0) {
         fprintf(codegen->out, "    sub $%ld, %%rsp\n", local_size);
diff --git a/src/type_checker.c b/src/type_checker.c
index 235d711..cc87832 100644
--- a/src/type_checker.c
+++ b/src/type_checker.c
@@ -108,6 +108,8 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
     assert(checker);
     assert(scope);
 
+    ast->scope = scope;
+
     switch (ast->kind) {
         case AST_NODE_TRANSLATION_UNIT: {
             list_item_t *item = list_head(ast->as_translation_unit.decls);
@@ -121,7 +123,7 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
 
         case AST_NODE_FN_DEF: {
             ast_fn_definition_t *fn_def = &ast->as_fn_def;
-            fn_def->scope = scope_push(scope);
+            ast->scope = scope_push(scope);
 
             type_resolve(fn_def->return_type);
             symbol_t *symbol =
@@ -136,20 +138,18 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
                 type_resolve(param->type);
                 symbol_t *symbol =
                     symbol_new(checker->arena, param->id, param->type);
-                scope_insert(fn_def->scope, symbol);
+                scope_insert(ast->scope, symbol);
 
                 item = list_next(item);
             }
 
             if (ast->as_fn_def.block != NULL) {
-                populate_scope(checker, fn_def->scope, ast->as_fn_def.block);
+                populate_scope(checker, ast->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) {
@@ -223,14 +223,12 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
                 symbol_new(checker->arena, id, ast->as_var_def.type);
 
             scope_insert(scope, symbol);
-            ast->as_var_def.scope = scope;
 
             populate_scope(checker, scope, ast->as_var_def.value);
             return;
         }
 
         case AST_NODE_REF: {
-            ast->as_ref.scope = scope;
             return;
         }
 
-- 
2.46.1


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v0] proposal: checker: set the eval type at the binary op expression
@ 2024-10-22  3:39 Carlos Maniero
  2024-10-22  3:40 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-22  3:39 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

I'm sending this patch to discuss how we can prepare our AST to handle
typing errors and to improve our codegen.

NOTICE: This is not a patch that should be applied; this is a proposal
so that we can understand what works or not and which path we will
follow.

ISSUE STATEMENT
===============

In my previous patchset, I had to identify what was the return type of a
deref operator (*), the main issue is, if I have a pointer of type
(u8**), if it is dereferenced one, it returns the type "u8*" if it is
dereferenced twice, it returns (u8).

But our AST does not provide this information, meaning that the codegen
needs to handle this logic.

MY PROPOSAL
===========

To add an new attribute on all the <expression> called *eval_type*, this
attribute will hold the type that the expression will produce.

Why?
----

- It simplifies the codegen
- There is a need to know the expressions result anyway for type
  checking.

HOW CLANG HANDLES IT?
=====================

I wrote the follow program in c:

int main() {
  return 1 + 2;
}

The AST was the following:

  `-FunctionDecl 0x55fb061d90f8 </tmp/a.c:1:1, line:3:1> line:1:5 main 'int ()'
    `-CompoundStmt 0x55fb061d9258 <col:12, line:3:1>
      `-ReturnStmt 0x55fb061d9248 <line:2:3, col:14>
        `-BinaryOperator 0x55fb061d9228 <col:10, col:14> 'int' '+'
          |-IntegerLiteral 0x55fb061d91e8 <col:10> 'int' 1
          `-IntegerLiteral 0x55fb061d9208 <col:14> 'int' 2

If you take a closer look at the BinaryyOperator, you will see it
specifies the operation result 'int'

        `-BinaryOperator 0x55fb061d9228 <col:10, col:14> 'int' '+'

ALTERNATIVES:
=============

- Make all ast_node_t to have an eval type.
  - This will require nodes like fn_def, translation_unit, if, while to
    have void eval_type, since they do not result anything. But this is
    confusing to have a function that has a "return_type" but doesn't
    have an "eval_type".

CONSIDERATIONS:
===============

1) I don't like the term eval_type, alternatives: result_type,
   expr_type.
2) We can use the populate_scope to make this as per my commit does,
   I like it because it does not require traversing the AST twice,
   although the name populate_scope no longer makes sense.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 src/ast.h                                  |  1 +
 src/checker.c                              | 53 ++++++++++++++--------
 src/codegen_x86_64.c                       |  3 ++
 src/main.c                                 |  3 ++
 src/pretty_print_ast.c                     |  8 +++-
 src/type.c                                 | 14 ++++++
 src/type.h                                 |  4 ++
 tests/olc/0002_binary_operator_addition.ol | 11 +++++
 tests/olc/0028_function_call.ol            |  2 +-
 tests/olc/0030_while_statement.ol          |  2 +-
 10 files changed, 80 insertions(+), 21 deletions(-)

diff --git a/src/ast.h b/src/ast.h
index e7d8ed8..7992045 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -146,6 +146,7 @@ typedef struct ast_binary_op
 {
     ast_node_meta_t meta;
     ast_binary_op_kind_t kind;
+    type_t *eval_type;
     ast_node_t *lhs;
     ast_node_t *rhs;
 } ast_binary_op_t;
diff --git a/src/checker.c b/src/checker.c
index 02341cc..90117d0 100644
--- a/src/checker.c
+++ b/src/checker.c
@@ -21,7 +21,10 @@
 #include <stdio.h>
 
 static void
-populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast);
+populate_scope(checker_t *checker,
+               scope_t *scope,
+               ast_node_t *ast,
+               type_t *eval_type);
 
 checker_t *
 checker_new(arena_t *arena)
@@ -86,6 +89,7 @@ type_resolve(type_t *type)
         case TYPE_PTR:
             type_resolve(type->as_ptr.type);
         case TYPE_PRIMITIVE:
+        case TYPE_VOID:
             break;
     }
 }
@@ -97,13 +101,16 @@ checker_check(checker_t *checker, ast_node_t *ast)
     assert(ast);
 
     scope_t *scope = scope_new(checker->arena);
-    populate_scope(checker, scope, ast);
+    populate_scope(checker, scope, ast, type_void());
 
     // TODO: traverse the ast tree to verify semantics
 }
 
 static void
-populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
+populate_scope(checker_t *checker,
+               scope_t *scope,
+               ast_node_t *ast,
+               type_t *eval_type)
 {
     assert(checker);
     assert(scope);
@@ -113,7 +120,8 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
             list_item_t *item = list_head(ast->as_translation_unit.decls);
 
             while (item != NULL) {
-                populate_scope(checker, scope, (ast_node_t *)item->value);
+                populate_scope(
+                    checker, scope, (ast_node_t *)item->value, eval_type);
                 item = list_next(item);
             }
             return;
@@ -124,8 +132,10 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
             fn_def->scope = scope_push(scope);
 
             type_resolve(fn_def->return_type);
+
             symbol_t *symbol =
                 symbol_new(checker->arena, fn_def->id, fn_def->return_type);
+
             scope_insert(scope, symbol);
 
             list_item_t *item = list_head(fn_def->params);
@@ -142,7 +152,10 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
             }
 
             if (ast->as_fn_def.block != NULL) {
-                populate_scope(checker, fn_def->scope, ast->as_fn_def.block);
+                populate_scope(checker,
+                               fn_def->scope,
+                               ast->as_fn_def.block,
+                               fn_def->return_type);
             }
             return;
         }
@@ -153,7 +166,8 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
             list_item_t *item = list_head(ast->as_fn_call.args);
 
             while (item != NULL) {
-                populate_scope(checker, scope, (ast_node_t *)item->value);
+                populate_scope(
+                    checker, scope, (ast_node_t *)item->value, eval_type);
                 item = list_next(item);
             }
 
@@ -161,42 +175,44 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
         }
 
         case AST_NODE_IF_STMT: {
-            populate_scope(checker, scope, ast->as_if_stmt.cond);
-            populate_scope(checker, scope, ast->as_if_stmt.then);
+            populate_scope(checker, scope, ast->as_if_stmt.cond, eval_type);
+            populate_scope(checker, scope, ast->as_if_stmt.then, eval_type);
 
             if (ast->as_if_stmt._else) {
-                populate_scope(checker, scope, ast->as_if_stmt._else);
+                populate_scope(
+                    checker, scope, ast->as_if_stmt._else, eval_type);
             }
 
             return;
         }
 
         case AST_NODE_WHILE_STMT: {
-            populate_scope(checker, scope, ast->as_while_stmt.cond);
-            populate_scope(checker, scope, ast->as_while_stmt.then);
+            populate_scope(checker, scope, ast->as_while_stmt.cond, eval_type);
+            populate_scope(checker, scope, ast->as_while_stmt.then, eval_type);
 
             return;
         }
 
         case AST_NODE_BINARY_OP: {
-            ast_binary_op_t bin_op = ast->as_bin_op;
+            ast->as_bin_op.eval_type = eval_type;
+            assert(eval_type);
 
-            populate_scope(checker, scope, bin_op.lhs);
-            populate_scope(checker, scope, bin_op.rhs);
+            populate_scope(checker, scope, ast->as_bin_op.lhs, eval_type);
+            populate_scope(checker, scope, ast->as_bin_op.rhs, eval_type);
             return;
         }
 
         case AST_NODE_UNARY_OP: {
             ast_unary_op_t unary_op = ast->as_unary_op;
 
-            populate_scope(checker, scope, unary_op.expr);
+            populate_scope(checker, scope, unary_op.expr, eval_type);
             return;
         }
 
         case AST_NODE_RETURN_STMT: {
             ast_return_stmt_t return_stmt = ast->as_return_stmt;
 
-            populate_scope(checker, scope, return_stmt.expr);
+            populate_scope(checker, scope, return_stmt.expr, eval_type);
             return;
         }
 
@@ -207,7 +223,8 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
             list_item_t *item = list_head(block.nodes);
 
             while (item != NULL) {
-                populate_scope(checker, scope, (ast_node_t *)item->value);
+                populate_scope(
+                    checker, scope, (ast_node_t *)item->value, eval_type);
                 item = list_next(item);
             }
 
@@ -225,7 +242,7 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
             scope_insert(scope, symbol);
             ast->as_var_def.scope = scope;
 
-            populate_scope(checker, scope, ast->as_var_def.value);
+            populate_scope(checker, scope, ast->as_var_def.value, eval_type);
             return;
         }
 
diff --git a/src/codegen_x86_64.c b/src/codegen_x86_64.c
index 4213571..3361510 100644
--- a/src/codegen_x86_64.c
+++ b/src/codegen_x86_64.c
@@ -913,6 +913,9 @@ type_to_bytes(type_t *type)
             assert(0 && "cannot calculate size of an unknown type: probably a "
                         "parser issue.");
         }
+        case TYPE_VOID: {
+            return 0;
+        }
     }
 
     assert(0 && "unreachable");
diff --git a/src/main.c b/src/main.c
index 685d8ce..08a0918 100644
--- a/src/main.c
+++ b/src/main.c
@@ -116,6 +116,9 @@ handle_dump_ast(cli_opts_t *opts)
 
     ast_node_t *ast = parser_parse_translation_unit(&parser);
 
+    checker_t *checker = checker_new(&arena);
+    checker_check(checker, ast);
+
     pretty_print_ast(ast);
 }
 
diff --git a/src/pretty_print_ast.c b/src/pretty_print_ast.c
index d476370..258e040 100644
--- a/src/pretty_print_ast.c
+++ b/src/pretty_print_ast.c
@@ -318,7 +318,13 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
 
             switch (binop.kind) {
                 case AST_BINOP_ADDITION: {
-                    node->name = "Binary_Operation (+)";
+                    char name[256];
+                    sprintf(name,
+                            "Binary_Operation (+) <type:" SV_FMT ">",
+                            SV_ARG(ast->as_bin_op.eval_type->id));
+                    node->name = (char *)arena_alloc(
+                        arena, sizeof(char) * (strlen(name) + 1));
+                    strcpy(node->name, name);
                     break;
                 }
                 case AST_BINOP_SUBTRACTION: {
diff --git a/src/type.c b/src/type.c
index 4a8d6f4..241e57c 100644
--- a/src/type.c
+++ b/src/type.c
@@ -17,6 +17,9 @@
 #include "type.h"
 #include "assert.h"
 
+char *void_str = "void";
+type_t global_void_type;
+
 type_t *
 type_new_unknown(arena_t *arena, string_view_t id)
 {
@@ -39,3 +42,14 @@ type_new_ptr(arena_t *arena, string_view_t id, type_t *ref_type)
     type->as_ptr.type = ref_type;
     return type;
 }
+
+type_t *
+type_void()
+{
+    if (global_void_type.id.chars != void_str) {
+        global_void_type.id.chars = void_str;
+        global_void_type.id.size = 4;
+        global_void_type.kind = TYPE_VOID;
+    }
+    return &global_void_type;
+}
diff --git a/src/type.h b/src/type.h
index d930a88..e4bf078 100644
--- a/src/type.h
+++ b/src/type.h
@@ -24,6 +24,7 @@ typedef union type type_t;
 typedef enum
 {
     TYPE_UNKNOWN,
+    TYPE_VOID,
     TYPE_PRIMITIVE,
     TYPE_PTR
 } type_kind_t;
@@ -72,6 +73,9 @@ typedef union type
 type_t *
 type_new_unknown(arena_t *arena, string_view_t id);
 
+type_t *
+type_void();
+
 type_t *
 type_new_ptr(arena_t *arena, string_view_t id, type_t *type);
 #endif
diff --git a/tests/olc/0002_binary_operator_addition.ol b/tests/olc/0002_binary_operator_addition.ol
index e408b47..c66f2e8 100644
--- a/tests/olc/0002_binary_operator_addition.ol
+++ b/tests/olc/0002_binary_operator_addition.ol
@@ -19,4 +19,15 @@ fn main(): u32 {
 }
 
 # TEST test_compile(exit_code=0)
+
 # TEST test_run_binary(exit_code=21)
+
+# TEST test_ast WITH
+# Translation_Unit
+# `-Function_Definition <name:main> <return:u32>
+#   `-Block
+#     `-Return_Statement
+#       `-Binary_Operation (+) <type:u32>
+#         |-Literal <kind:u32> <value:10>
+#         `-Literal <kind:u32> <value:11>
+# END
diff --git a/tests/olc/0028_function_call.ol b/tests/olc/0028_function_call.ol
index b32fbbf..1bd6ad9 100644
--- a/tests/olc/0028_function_call.ol
+++ b/tests/olc/0028_function_call.ol
@@ -38,7 +38,7 @@ fn add(a: u32, b: u64): u8 {
 #   |-Param_Definition <name:b> <type:u64>
 #   `-Block
 #     `-Return_Statement
-#       `-Binary_Operation (+)
+#       `-Binary_Operation (+) <type:u8>
 #         |-Reference <name:a>
 #         `-Reference <name:b>
 # END
diff --git a/tests/olc/0030_while_statement.ol b/tests/olc/0030_while_statement.ol
index 0ce0185..09ba7bd 100644
--- a/tests/olc/0030_while_statement.ol
+++ b/tests/olc/0030_while_statement.ol
@@ -40,7 +40,7 @@ fn main(): u32 {
 #     | `-Block
 #     |   `-Binary_Operation (=)
 #     |     |-Reference <name:i>
-#     |     `-Binary_Operation (+)
+#     |     `-Binary_Operation (+) <type:u32>
 #     |       |-Reference <name:i>
 #     |       `-Literal <kind:u32> <value:1>
 #     `-Return_Statement
-- 
2.46.1


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v2 1/1] codegen: x64: deref returns pointer value
@ 2024-10-19 14:10 Carlos Maniero
  2024-10-19 14:11 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-19 14:10 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

Deref is context dependent when doing an assignment:

  *a = 1

It is expected for the deref codegen to return the pointer location, so
than the assignment binop would be able to assign a new value at that
location.

On another hand, when performing:

  return *a

It is expected for deref to actually returns the pointer location value.

At this point, we don't have an easy way to get the operation result
type so there is a tricky logic to determine if the unnary expression is
returning a pointer or a value, ie: when dereferencing "var b: u16**"
once (*b) the result size is 8 bits as it returns another pointer, but
by dereferencing it twice (**b) we are returning 2 bits since at this
point we are returning a u16.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 src/codegen_x86_64.c                 | 88 +++++++++++++++++++++++++---
 src/parser.c                         | 11 ++--
 tests/olc/0038_pointers_deref.ol     | 24 ++++++++
 tests/olc/0039_pointer_of_pointer.ol | 55 +++++++++++++++++
 4 files changed, 164 insertions(+), 14 deletions(-)
 create mode 100644 tests/olc/0038_pointers_deref.ol
 create mode 100644 tests/olc/0039_pointer_of_pointer.ol

diff --git a/src/codegen_x86_64.c b/src/codegen_x86_64.c
index deb7e24..4213571 100644
--- a/src/codegen_x86_64.c
+++ b/src/codegen_x86_64.c
@@ -52,6 +52,8 @@ typedef enum x86_64_register_type
     REG_R15
 } x86_64_register_type_t;
 
+typedef size_t size_in_bytes_t;
+
 /**
  * Arch/ABI      arg1  arg2  arg3  arg4  arg5  arg6  arg7  Notes
  * ──────────────────────────────────────────────────────────────
@@ -76,6 +78,14 @@ codegen_x86_64_put_stack_offset(codegen_x86_64_t *codegen,
 static size_t
 codegen_x86_64_get_stack_offset(codegen_x86_64_t *codegen, symbol_t *symbol);
 
+static size_in_bytes_t
+codegen_x86_64_emit_unary_deref_address(codegen_x86_64_t *codegen,
+                                        ast_unary_op_t *unary_op);
+
+static size_in_bytes_t
+codegen_x86_64_emit_unary_deref_value(codegen_x86_64_t *codegen,
+                                      ast_unary_op_t *unary_op);
+
 static size_t
 type_to_bytes(type_t *type);
 
@@ -126,8 +136,6 @@ codegen_x86_64_get_next_label(codegen_x86_64_t *codegen)
     return ++codegen->label_index;
 }
 
-typedef size_t size_in_bytes_t;
-
 static size_in_bytes_t
 codegen_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr_node)
 {
@@ -619,7 +627,8 @@ codegen_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr_node)
                                        AST_UNARY_DEREFERENCE &&
                                    "unsupported assignment lhs");
 
-                            codegen_x86_64_emit_expression(codegen, bin_op.lhs);
+                            codegen_x86_64_emit_unary_deref_address(
+                                codegen, &bin_op.lhs->as_unary_op);
 
                             fprintf(codegen->out, "    push %%rax\n");
 
@@ -679,12 +688,8 @@ codegen_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr_node)
                     return 8;
                 }
                 case AST_UNARY_DEREFERENCE: {
-                    // FIXME: support dereference of dereference (**)
-                    assert(unary_op.expr->kind == AST_NODE_REF &&
-                           "unsupported unary expression for dereference (*)");
-
-                    return codegen_x86_64_emit_expression(codegen,
-                                                          unary_op.expr);
+                    return codegen_x86_64_emit_unary_deref_value(codegen,
+                                                                 &unary_op);
                 }
                 default: {
                     assert(0 && "unsupported unary operation");
@@ -829,6 +834,71 @@ codegen_x86_64_emit_if(codegen_x86_64_t *codegen, ast_if_stmt_t if_stmt)
     fprintf(codegen->out, ".L%ld:\n", end_else_label);
 }
 
+static size_in_bytes_t
+codegen_x86_64_emit_unary_deref_address(codegen_x86_64_t *codegen,
+                                        ast_unary_op_t *unary_op)
+{
+    assert(unary_op->kind == AST_UNARY_DEREFERENCE);
+
+    if (unary_op->expr->kind == AST_NODE_UNARY_OP) {
+        // dive into the AST until it finds a ref
+        size_in_bytes_t size = codegen_x86_64_emit_unary_deref_address(
+            codegen, &unary_op->expr->as_unary_op);
+
+        fprintf(codegen->out, "    mov (%%rax), %%rax\n");
+
+        return size;
+    }
+
+    assert(unary_op->expr->kind == AST_NODE_REF);
+    return codegen_x86_64_emit_expression(codegen, unary_op->expr);
+}
+
+static size_in_bytes_t
+codegen_x86_64_emit_unary_deref_value(codegen_x86_64_t *codegen,
+                                      ast_unary_op_t *unary_op)
+{
+    codegen_x86_64_emit_unary_deref_address(codegen, unary_op);
+
+    ast_node_t *expr = unary_op->expr;
+
+    size_t deref_levels = 0;
+
+    while (expr->kind != AST_NODE_REF) {
+        assert(expr->kind == AST_NODE_UNARY_OP &&
+               expr->as_unary_op.kind == AST_UNARY_DEREFERENCE);
+
+        expr = expr->as_unary_op.expr;
+        deref_levels++;
+    }
+
+    ast_ref_t ref = expr->as_ref;
+
+    symbol_t *symbol = scope_lookup(ref.scope, ref.id);
+
+    type_t *t_result = symbol->type;
+
+    assert(t_result->kind == TYPE_PTR);
+
+    // FIXME: Identifies the operation result type based on how many times the
+    //        deref operator was used. The semantics should provide the result
+    //        type of an operation, so then the codegen does not need to care
+    //        about it.
+    while (deref_levels-- > 0) {
+        t_result = symbol->type->as_ptr.type;
+
+        assert(symbol->type->kind == TYPE_PTR);
+    }
+
+    size_in_bytes_t deref_size = type_to_bytes(t_result->as_ptr.type);
+
+    fprintf(codegen->out,
+            "    mov (%%rax), %s\n",
+            get_reg_for(REG_ACCUMULATOR, deref_size));
+
+    return deref_size;
+}
+
 static size_t
 type_to_bytes(type_t *type)
 {
diff --git a/src/parser.c b/src/parser.c
index d26f266..386f60b 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -538,13 +538,12 @@ parser_parse_type(parser_t *parser)
         return NULL;
     }
 
-    token_t ptr_token;
+    type_t *type = type_new_unknown(parser->arena, token.value);
 
+    token_t ptr_token;
     lexer_peek_next(parser->lexer, &ptr_token);
 
-    type_t *type = type_new_unknown(parser->arena, token.value);
-
-    if (ptr_token.kind == TOKEN_STAR) {
+    while (ptr_token.kind == TOKEN_STAR) {
         if (!skip_expected_token(parser, TOKEN_STAR)) {
             return NULL;
         }
@@ -553,7 +552,9 @@ parser_parse_type(parser_t *parser)
         ptr_id.size =
             ptr_token.value.chars - token.value.chars + ptr_token.value.size;
 
-        return type_new_ptr(parser->arena, ptr_id, type);
+        type = type_new_ptr(parser->arena, ptr_id, type);
+
+        lexer_peek_next(parser->lexer, &ptr_token);
     }
 
     return type;
diff --git a/tests/olc/0038_pointers_deref.ol b/tests/olc/0038_pointers_deref.ol
new file mode 100644
index 0000000..cef91b2
--- /dev/null
+++ b/tests/olc/0038_pointers_deref.ol
@@ -0,0 +1,24 @@
+# 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: u64 = 0
+  var b: u64* = &a
+  return *b
+}
+
+# TEST test_compile(exit_code=0)
+
+# TEST test_run_binary(exit_code=0)
diff --git a/tests/olc/0039_pointer_of_pointer.ol b/tests/olc/0039_pointer_of_pointer.ol
new file mode 100644
index 0000000..e98178a
--- /dev/null
+++ b/tests/olc/0039_pointer_of_pointer.ol
@@ -0,0 +1,55 @@
+# 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
+  var b: u32* = &a
+  var c: u32** = &b
+  var d: u32 = 41
+
+  **c = 42
+
+  if a != 42 {
+    return 1
+  }
+
+  if a != *b {
+    return 2
+  }
+
+  if *b != **c {
+    return 3
+  }
+
+  if b != &a {
+    return 4
+  }
+
+  if *c != &a {
+    return 5
+  }
+
+  *c = &d
+
+  if *b != 41 {
+    return 6
+  }
+
+  return 0
+}
+
+# TEST test_compile(exit_code=0)
+
+# TEST test_run_binary(exit_code=0)
-- 
2.46.1


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1 1/3] lexer: spec: add extern keyword for function def
@ 2024-10-17  2:04 Johnny Richard
  2024-10-17  0:07 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Johnny Richard @ 2024-10-17  2:04 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 docs/info/olang.ebnf                |  2 +-
 src/lexer.c                         |  5 ++++
 src/lexer.h                         |  1 +
 tests/olc/0037_variable_overflow.ol | 39 +++++++++++++++++++++++++++++
 4 files changed, 46 insertions(+), 1 deletion(-)
 create mode 100644 tests/olc/0037_variable_overflow.ol

diff --git a/docs/info/olang.ebnf b/docs/info/olang.ebnf
index 0bc7eb5..5bd2be6 100644
--- a/docs/info/olang.ebnf
+++ b/docs/info/olang.ebnf
@@ -11,7 +11,7 @@
 <variable-initializer> ::= '=' <ows> <expression>
 
 (* Functions *)
-<function-definition> ::= 'fn' <ws> <function-name> <ows> '(' ( <ows> | <ows> <function-params> <ows> ) ')' <ows> ':' <ows> <return-type> <ows> <function-body>
+<function-definition> ::= ('extern' <ws>)? 'fn' <ws> <function-name> <ows> '(' ( <ows> | <ows> <function-params> <ows> ) ')' <ows> ':' <ows> <return-type> <ows> <function-body>?
 <function-name>       ::= <identifier>
 <function-params>     ::= <identifier> <ows> ':' <ows> <type> ( <ows> ',' <ows> <function-params>)*
 <return-type>         ::= <type>
diff --git a/src/lexer.c b/src/lexer.c
index 1a6c24b..ddfe42a 100644
--- a/src/lexer.c
+++ b/src/lexer.c
@@ -313,6 +313,7 @@ static char *token_kind_str_table[] = {
     [TOKEN_ELSE] = "else",
     [TOKEN_WHILE] = "while",
     [TOKEN_VAR] = "var",
+    [TOKEN_EXTERN] = "extern",
     [TOKEN_LF] = "line_feed",
     [TOKEN_OPAREN] = "(",
     [TOKEN_CPAREN] = ")",
@@ -489,6 +490,10 @@ lexer_str_to_token_kind(string_view_t text)
         return TOKEN_VAR;
     }
 
+    if (string_view_eq_to_cstr(text, "extern")) {
+        return TOKEN_EXTERN;
+    }
+
     if (string_view_eq_to_cstr(text, "return")) {
         return TOKEN_RETURN;
     }
diff --git a/src/lexer.h b/src/lexer.h
index 774f619..bba15fb 100644
--- a/src/lexer.h
+++ b/src/lexer.h
@@ -53,6 +53,7 @@ typedef enum token_kind
     TOKEN_ELSE,
     TOKEN_WHILE,
     TOKEN_VAR,
+    TOKEN_EXTERN,
 
     // Equality operators
     TOKEN_CMP_EQ,
diff --git a/tests/olc/0037_variable_overflow.ol b/tests/olc/0037_variable_overflow.ol
new file mode 100644
index 0000000..0e3c302
--- /dev/null
+++ b/tests/olc/0037_variable_overflow.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/>.
+
+extern fn putchar(c: u32): u32
+
+fn main(): u32 {
+  return putchar(111)
+}
+
+# XTEST test_compile(exit_code=0)
+#
+# XTEST test_run_binary(exit_code=0)
+#
+# XTEST test_ast WITH
+# Translation_Unit
+# |-Function_Definition <name:putchar> <return:u32> <extern>
+# | `-Param_Definition <name:c> <type:u32>
+# `-Function_Definition <name:main> <return:u32>
+#   `-Block
+#     `-Return_Statement
+#       `-Function_Call <name:putchar>
+#         `-Literal <kind:u32> <value:111>
+# END
+#
+# TEST test_contains_tokens WITH
+# ./0037_variable_overflow.ol:16:1: <extern>
+# END
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1] codegen: allow function call at the block level
@ 2024-10-17  0:46 Carlos Maniero
  2024-10-17  0:47 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-17  0:46 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

The extern test was also renamed to match its intention.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 src/codegen_x86_64.c                                |  3 ++-
 ...037_variable_overflow.ol => 0037_extern_call.ol} | 13 ++++++++-----
 2 files changed, 10 insertions(+), 6 deletions(-)
 rename tests/olc/{0037_variable_overflow.ol => 0037_extern_call.ol} (82%)

diff --git a/src/codegen_x86_64.c b/src/codegen_x86_64.c
index c4cef3c..deb7e24 100644
--- a/src/codegen_x86_64.c
+++ b/src/codegen_x86_64.c
@@ -745,7 +745,8 @@ codegen_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
                 break;
             }
 
-            case AST_NODE_BINARY_OP: {
+            case AST_NODE_BINARY_OP:
+            case AST_NODE_FN_CALL: {
                 codegen_x86_64_emit_expression(codegen, node);
                 break;
             }
diff --git a/tests/olc/0037_variable_overflow.ol b/tests/olc/0037_extern_call.ol
similarity index 82%
rename from tests/olc/0037_variable_overflow.ol
rename to tests/olc/0037_extern_call.ol
index be36e77..68fe139 100644
--- a/tests/olc/0037_variable_overflow.ol
+++ b/tests/olc/0037_extern_call.ol
@@ -16,12 +16,14 @@
 extern fn putchar(c: u32): u32
 
 fn main(): u32 {
-  return putchar(111)
+  putchar(111)
+
+  return 0
 }
 
 # TEST test_compile(exit_code=0)
 #
-# TEST test_run_binary(exit_code=111)
+# TEST test_run_binary(exit_code=0)
 #
 # TEST test_ast WITH
 # Translation_Unit
@@ -29,11 +31,12 @@ fn main(): u32 {
 # | `-Param_Definition <name:c> <type:u32>
 # `-Function_Definition <name:main> <return:u32>
 #   `-Block
+#     |-Function_Call <name:putchar>
+#     | `-Literal <kind:u32> <value:111>
 #     `-Return_Statement
-#       `-Function_Call <name:putchar>
-#         `-Literal <kind:u32> <value:111>
+#       `-Literal <kind:u32> <value:0>
 # END
 #
 # TEST test_contains_tokens WITH
-# ./0037_variable_overflow.ol:16:1: <extern>
+# ./0037_extern_call.ol:16:1: <extern>
 # END

base-commit: 9c38120024f10cbfcedb8525ea7bd6c98d7f7b1e
-- 
2.46.1


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1 3/3] codegen: remove linux from codegen namespace
@ 2024-10-17  0:20 Johnny Richard
  2024-10-16 22:22 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Johnny Richard @ 2024-10-17  0:20 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Since the codegen is OS dependent, we don't need to tie the compilation
to linux.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 ...egen_linux_aarch64.c => codegen_aarch64.c} |  16 +-
 ...egen_linux_aarch64.h => codegen_aarch64.h} |   2 +-
 ...odegen_linux_x86_64.c => codegen_x86_64.c} | 229 +++++++-----------
 ...odegen_linux_x86_64.h => codegen_x86_64.h} |   6 +-
 src/main.c                                    |  14 +-
 5 files changed, 109 insertions(+), 158 deletions(-)
 rename src/{codegen_linux_aarch64.c => codegen_aarch64.c} (88%)
 rename src/{codegen_linux_aarch64.h => codegen_aarch64.h} (91%)
 rename src/{codegen_linux_x86_64.c => codegen_x86_64.c} (78%)
 rename src/{codegen_linux_x86_64.h => codegen_x86_64.h} (82%)

diff --git a/src/codegen_linux_aarch64.c b/src/codegen_aarch64.c
similarity index 88%
rename from src/codegen_linux_aarch64.c
rename to src/codegen_aarch64.c
index d8187ab..1de238d 100644
--- a/src/codegen_linux_aarch64.c
+++ b/src/codegen_aarch64.c
@@ -18,7 +18,7 @@
 #include <stdint.h>
 #include <stdio.h>
 
-#include "codegen_linux_aarch64.h"
+#include "codegen_aarch64.h"
 #include "list.h"
 
 #define SYS_exit (93)
@@ -36,15 +36,15 @@
  */
 
 static void
-codegen_linux_aarch64_emit_start_entrypoint(FILE *out);
+codegen_aarch64_emit_start_entrypoint(FILE *out);
 
 static void
-codegen_linux_aarch64_emit_function(FILE *out, ast_fn_definition_t *fn);
+codegen_aarch64_emit_function(FILE *out, ast_fn_definition_t *fn);
 
 void
-codegen_linux_aarch64_emit_translation_unit(FILE *out, ast_node_t *node)
+codegen_aarch64_emit_translation_unit(FILE *out, ast_node_t *node)
 {
-    codegen_linux_aarch64_emit_start_entrypoint(out);
+    codegen_aarch64_emit_start_entrypoint(out);
 
     assert(node->kind == AST_NODE_TRANSLATION_UNIT);
     ast_translation_unit_t translation_unit = node->as_translation_unit;
@@ -58,7 +58,7 @@ codegen_linux_aarch64_emit_translation_unit(FILE *out, ast_node_t *node)
 
         if (decl->kind == AST_NODE_FN_DEF) {
             ast_fn_definition_t fn = decl->as_fn_def;
-            codegen_linux_aarch64_emit_function(out, &fn);
+            codegen_aarch64_emit_function(out, &fn);
 
             main_found = main_found || string_view_eq_to_cstr(fn.id, "main");
         } else {
@@ -72,7 +72,7 @@ codegen_linux_aarch64_emit_translation_unit(FILE *out, ast_node_t *node)
 }
 
 static void
-codegen_linux_aarch64_emit_start_entrypoint(FILE *out)
+codegen_aarch64_emit_start_entrypoint(FILE *out)
 {
     fprintf(out, ".text\n");
     fprintf(out, ".globl _start\n\n");
@@ -84,7 +84,7 @@ codegen_linux_aarch64_emit_start_entrypoint(FILE *out)
 }
 
 static void
-codegen_linux_aarch64_emit_function(FILE *out, ast_fn_definition_t *fn)
+codegen_aarch64_emit_function(FILE *out, ast_fn_definition_t *fn)
 {
     ast_node_t *block_node = fn->block;
     assert(block_node->kind == AST_NODE_BLOCK);
diff --git a/src/codegen_linux_aarch64.h b/src/codegen_aarch64.h
similarity index 91%
rename from src/codegen_linux_aarch64.h
rename to src/codegen_aarch64.h
index 03e2f46..3f7e0b0 100644
--- a/src/codegen_linux_aarch64.h
+++ b/src/codegen_aarch64.h
@@ -20,6 +20,6 @@
 #include "ast.h"
 
 void
-codegen_linux_aarch64_emit_translation_unit(FILE *out, ast_node_t *prog);
+codegen_aarch64_emit_translation_unit(FILE *out, ast_node_t *prog);
 
 #endif /* CODEGEN_LINUX_AARCH64_H */
diff --git a/src/codegen_linux_x86_64.c b/src/codegen_x86_64.c
similarity index 78%
rename from src/codegen_linux_x86_64.c
rename to src/codegen_x86_64.c
index 6348dad..6758fc1 100644
--- a/src/codegen_linux_x86_64.c
+++ b/src/codegen_x86_64.c
@@ -18,7 +18,7 @@
 #include <stdint.h>
 #include <stdio.h>
 
-#include "codegen_linux_x86_64.h"
+#include "codegen_x86_64.h"
 #include "list.h"
 #include "map.h"
 #include "scope.h"
@@ -62,20 +62,19 @@ static int x86_call_args[X86_CALL_ARG_SIZE] = { REG_DEST_IDX, REG_SRC_IDX,
                                                 REG_R8,       REG_R9 };
 
 static void
-codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen,
-                                   ast_fn_definition_t *fn);
+codegen_x86_64_emit_function(codegen_x86_64_t *codegen,
+                             ast_fn_definition_t *fn);
 
 static void
-codegen_linux_x86_64_emit_if(codegen_x86_64_t *codegen, ast_if_stmt_t is_stmt);
+codegen_x86_64_emit_if(codegen_x86_64_t *codegen, ast_if_stmt_t is_stmt);
 
 static void
-codegen_linux_x86_64_put_stack_offset(codegen_x86_64_t *codegen,
-                                      symbol_t *symbol,
-                                      size_t offset);
+codegen_x86_64_put_stack_offset(codegen_x86_64_t *codegen,
+                                symbol_t *symbol,
+                                size_t offset);
 
 static size_t
-codegen_linux_x86_64_get_stack_offset(codegen_x86_64_t *codegen,
-                                      symbol_t *symbol);
+codegen_x86_64_get_stack_offset(codegen_x86_64_t *codegen, symbol_t *symbol);
 
 static size_t
 type_to_bytes(type_t *type);
@@ -84,7 +83,7 @@ static char *
 get_reg_for(x86_64_register_type_t type, size_t bytes);
 
 void
-codegen_linux_x86_64_init(codegen_x86_64_t *codegen, arena_t *arena, FILE *out)
+codegen_x86_64_init(codegen_x86_64_t *codegen, arena_t *arena, FILE *out)
 {
     assert(codegen);
     assert(arena);
@@ -96,8 +95,8 @@ codegen_linux_x86_64_init(codegen_x86_64_t *codegen, arena_t *arena, FILE *out)
 }
 
 void
-codegen_linux_x86_64_emit_translation_unit(codegen_x86_64_t *codegen,
-                                           ast_node_t *node)
+codegen_x86_64_emit_translation_unit(codegen_x86_64_t *codegen,
+                                     ast_node_t *node)
 {
     codegen->label_index = 0;
     fprintf(codegen->out, ".text\n");
@@ -112,7 +111,7 @@ codegen_linux_x86_64_emit_translation_unit(codegen_x86_64_t *codegen,
 
         if (decl->kind == AST_NODE_FN_DEF) {
             ast_fn_definition_t fn = decl->as_fn_def;
-            codegen_linux_x86_64_emit_function(codegen, &fn);
+            codegen_x86_64_emit_function(codegen, &fn);
         } else {
             assert(0 && "translation unit only supports function declarations");
         }
@@ -122,7 +121,7 @@ codegen_linux_x86_64_emit_translation_unit(codegen_x86_64_t *codegen,
 }
 
 static size_t
-codegen_linux_x86_64_get_next_label(codegen_x86_64_t *codegen)
+codegen_x86_64_get_next_label(codegen_x86_64_t *codegen)
 {
     return ++codegen->label_index;
 }
@@ -130,8 +129,7 @@ codegen_linux_x86_64_get_next_label(codegen_x86_64_t *codegen)
 typedef size_t size_in_bytes_t;
 
 static size_in_bytes_t
-codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
-                                     ast_node_t *expr_node)
+codegen_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr_node)
 {
     switch (expr_node->kind) {
         case AST_NODE_LITERAL: {
@@ -148,8 +146,7 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
             symbol_t *symbol = scope_lookup(ref.scope, ref.id);
             assert(symbol);
 
-            size_t offset =
-                codegen_linux_x86_64_get_stack_offset(codegen, symbol);
+            size_t offset = codegen_x86_64_get_stack_offset(codegen, symbol);
 
             size_t bytes = type_to_bytes(symbol->type);
 
@@ -173,7 +170,7 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
 
                 ast_node_t *arg_node = (ast_node_t *)item->value;
 
-                codegen_linux_x86_64_emit_expression(codegen, arg_node);
+                codegen_x86_64_emit_expression(codegen, arg_node);
 
                 fprintf(codegen->out,
                         "    push %s\n",
@@ -197,14 +194,12 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
                 case AST_BINOP_ADDITION: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t rhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.rhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t lhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.lhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.lhs);
 
                     size_in_bytes_t expr_bytes =
                         bytes_max(rhs_bytes, lhs_bytes);
@@ -220,14 +215,12 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
                 case AST_BINOP_MULTIPLICATION: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t rhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.rhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t lhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.lhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.lhs);
 
                     size_in_bytes_t expr_bytes =
                         bytes_max(rhs_bytes, lhs_bytes);
@@ -242,14 +235,12 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
                 case AST_BINOP_DIVISION: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t rhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.rhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t lhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.lhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.lhs);
 
                     size_in_bytes_t expr_bytes =
                         bytes_max(rhs_bytes, lhs_bytes);
@@ -265,14 +256,12 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
                 case AST_BINOP_REMINDER: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t rhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.rhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t lhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.lhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.lhs);
 
                     size_in_bytes_t expr_bytes =
                         bytes_max(rhs_bytes, lhs_bytes);
@@ -292,14 +281,12 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
                 case AST_BINOP_SUBTRACTION: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t rhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.rhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t lhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.lhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.lhs);
 
                     size_in_bytes_t expr_bytes =
                         bytes_max(rhs_bytes, lhs_bytes);
@@ -315,14 +302,12 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
                 case AST_BINOP_CMP_EQ: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t rhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.rhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t lhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.lhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.lhs);
 
                     size_in_bytes_t expr_bytes =
                         bytes_max(rhs_bytes, lhs_bytes);
@@ -342,14 +327,12 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
                 case AST_BINOP_CMP_LT: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t rhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.rhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t lhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.lhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.lhs);
 
                     size_in_bytes_t expr_bytes =
                         bytes_max(rhs_bytes, lhs_bytes);
@@ -369,14 +352,12 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
                 case AST_BINOP_CMP_GT: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t rhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.rhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t lhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.lhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.lhs);
 
                     size_in_bytes_t expr_bytes =
                         bytes_max(rhs_bytes, lhs_bytes);
@@ -396,14 +377,12 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
                 case AST_BINOP_CMP_NEQ: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t rhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.rhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t lhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.lhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.lhs);
 
                     size_in_bytes_t expr_bytes =
                         bytes_max(rhs_bytes, lhs_bytes);
@@ -423,14 +402,12 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
                 case AST_BINOP_CMP_LEQ: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t rhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.rhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t lhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.lhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.lhs);
 
                     size_in_bytes_t expr_bytes =
                         bytes_max(rhs_bytes, lhs_bytes);
@@ -450,14 +427,12 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
                 case AST_BINOP_CMP_GEQ: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t rhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.rhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t lhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.lhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.lhs);
 
                     size_in_bytes_t expr_bytes =
                         bytes_max(rhs_bytes, lhs_bytes);
@@ -476,13 +451,12 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
                 }
                 case AST_BINOP_BITWISE_LSHIFT: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    codegen_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t lhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.lhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.lhs);
 
                     fprintf(codegen->out, "    pop %%rcx\n");
                     fprintf(codegen->out,
@@ -493,13 +467,12 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
                 }
                 case AST_BINOP_BITWISE_RSHIFT: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    codegen_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t lhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.lhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.lhs);
 
                     fprintf(codegen->out, "    pop %%rcx\n");
                     fprintf(codegen->out,
@@ -511,14 +484,12 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
                 case AST_BINOP_BITWISE_XOR: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t rhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.rhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t lhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.lhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.lhs);
 
                     size_in_bytes_t expr_bytes =
                         bytes_max(rhs_bytes, lhs_bytes);
@@ -534,14 +505,12 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
                 case AST_BINOP_BITWISE_AND: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t rhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.rhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t lhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.lhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.lhs);
 
                     size_in_bytes_t expr_bytes =
                         bytes_max(rhs_bytes, lhs_bytes);
@@ -557,14 +526,12 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
                 case AST_BINOP_BITWISE_OR: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t rhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.rhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t lhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.lhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.lhs);
 
                     size_in_bytes_t expr_bytes =
                         bytes_max(rhs_bytes, lhs_bytes);
@@ -578,13 +545,11 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
                     return expr_bytes;
                 }
                 case AST_BINOP_LOGICAL_AND: {
-                    size_t label_exit =
-                        codegen_linux_x86_64_get_next_label(codegen);
+                    size_t label_exit = codegen_x86_64_get_next_label(codegen);
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t lhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.lhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.lhs);
                     fprintf(codegen->out,
                             "    cmp $0, %s\n",
                             get_reg_for(REG_ACCUMULATOR, lhs_bytes));
@@ -592,8 +557,7 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t rhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.rhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out,
                             "    cmp $0, %s\n",
                             get_reg_for(REG_ACCUMULATOR, rhs_bytes));
@@ -604,15 +568,12 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
                     return 1;
                 }
                 case AST_BINOP_LOGICAL_OR: {
-                    size_t label_t =
-                        codegen_linux_x86_64_get_next_label(codegen);
-                    size_t label_f =
-                        codegen_linux_x86_64_get_next_label(codegen);
+                    size_t label_t = codegen_x86_64_get_next_label(codegen);
+                    size_t label_f = codegen_x86_64_get_next_label(codegen);
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t lhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.lhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.lhs);
                     fprintf(codegen->out,
                             "    cmp $0, %s\n",
                             get_reg_for(REG_ACCUMULATOR, lhs_bytes));
@@ -620,8 +581,7 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     size_in_bytes_t rhs_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             bin_op.rhs);
+                        codegen_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out,
                             "    cmp $0, %s\n",
                             get_reg_for(REG_ACCUMULATOR, rhs_bytes));
@@ -642,12 +602,10 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
                             symbol_t *symbol = scope_lookup(scope, ref.id);
                             assert(symbol);
 
-                            size_t offset =
-                                codegen_linux_x86_64_get_stack_offset(codegen,
-                                                                      symbol);
+                            size_t offset = codegen_x86_64_get_stack_offset(
+                                codegen, symbol);
 
-                            codegen_linux_x86_64_emit_expression(codegen,
-                                                                 bin_op.rhs);
+                            codegen_x86_64_emit_expression(codegen, bin_op.rhs);
 
                             size_t type_size = type_to_bytes(symbol->type);
                             fprintf(codegen->out,
@@ -661,14 +619,12 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
                                        AST_UNARY_DEREFERENCE &&
                                    "unsupported assignment lhs");
 
-                            codegen_linux_x86_64_emit_expression(codegen,
-                                                                 bin_op.lhs);
+                            codegen_x86_64_emit_expression(codegen, bin_op.lhs);
 
                             fprintf(codegen->out, "    push %%rax\n");
 
-                            size_t type_size =
-                                codegen_linux_x86_64_emit_expression(
-                                    codegen, bin_op.rhs);
+                            size_t type_size = codegen_x86_64_emit_expression(
+                                codegen, bin_op.rhs);
 
                             fprintf(codegen->out, "    pop %%rdx\n");
 
@@ -698,8 +654,7 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
             switch (unary_op.kind) {
                 case AST_UNARY_BITWISE_NOT: {
                     size_in_bytes_t expr_bytes =
-                        codegen_linux_x86_64_emit_expression(codegen,
-                                                             unary_op.expr);
+                        codegen_x86_64_emit_expression(codegen, unary_op.expr);
 
                     fprintf(codegen->out,
                             "    not %s\n",
@@ -717,7 +672,7 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
                     assert(symbol);
 
                     size_t offset =
-                        codegen_linux_x86_64_get_stack_offset(codegen, symbol);
+                        codegen_x86_64_get_stack_offset(codegen, symbol);
 
                     fprintf(
                         codegen->out, "    lea -%ld(%%rbp), %%rax\n", offset);
@@ -728,8 +683,8 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
                     assert(unary_op.expr->kind == AST_NODE_REF &&
                            "unsupported unary expression for dereference (*)");
 
-                    return codegen_linux_x86_64_emit_expression(codegen,
-                                                                unary_op.expr);
+                    return codegen_x86_64_emit_expression(codegen,
+                                                          unary_op.expr);
                 }
                 default: {
                     assert(0 && "unsupported unary operation");
@@ -743,7 +698,7 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
     }
 }
 static void
-codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
+codegen_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
 {
     size_t block_offset = codegen->base_offset;
     size_t nodes_len = list_size(block->nodes);
@@ -756,7 +711,7 @@ codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
 
                 ast_node_t *expr = return_stmt.expr;
 
-                codegen_linux_x86_64_emit_expression(codegen, expr);
+                codegen_x86_64_emit_expression(codegen, expr);
 
                 fprintf(codegen->out, "    mov %%rbp, %%rsp\n");
                 fprintf(codegen->out, "    pop %%rbp\n");
@@ -775,12 +730,11 @@ codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
                 size_t type_size = type_to_bytes(symbol->type);
                 codegen->base_offset += type_size;
 
-                codegen_linux_x86_64_put_stack_offset(
+                codegen_x86_64_put_stack_offset(
                     codegen, symbol, codegen->base_offset);
 
                 if (var_def.value) {
-                    codegen_linux_x86_64_emit_expression(codegen,
-                                                         var_def.value);
+                    codegen_x86_64_emit_expression(codegen, var_def.value);
                 }
 
                 fprintf(codegen->out,
@@ -792,12 +746,12 @@ codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
             }
 
             case AST_NODE_BINARY_OP: {
-                codegen_linux_x86_64_emit_expression(codegen, node);
+                codegen_x86_64_emit_expression(codegen, node);
                 break;
             }
 
             case AST_NODE_IF_STMT: {
-                codegen_linux_x86_64_emit_if(codegen, node->as_if_stmt);
+                codegen_x86_64_emit_if(codegen, node->as_if_stmt);
                 break;
             }
 
@@ -807,12 +761,11 @@ codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
                 ast_node_t *cond = while_stmt.cond;
                 ast_node_t *then = while_stmt.then;
 
-                size_t begin_label =
-                    codegen_linux_x86_64_get_next_label(codegen);
-                size_t end_label = codegen_linux_x86_64_get_next_label(codegen);
+                size_t begin_label = codegen_x86_64_get_next_label(codegen);
+                size_t end_label = codegen_x86_64_get_next_label(codegen);
 
                 fprintf(codegen->out, ".L%ld:\n", begin_label);
-                codegen_linux_x86_64_emit_expression(codegen, cond);
+                codegen_x86_64_emit_expression(codegen, cond);
                 fprintf(codegen->out, "    cmp $1, %%rax\n");
                 fprintf(codegen->out, "    jnz .L%ld\n", end_label);
 
@@ -820,7 +773,7 @@ codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
                        "invalid while-then block");
                 ast_block_t then_block = then->as_block;
 
-                codegen_linux_x86_64_emit_block(codegen, &then_block);
+                codegen_x86_64_emit_block(codegen, &then_block);
 
                 fprintf(codegen->out, "    jmp .L%ld\n", begin_label);
                 fprintf(codegen->out, ".L%ld:\n", end_label);
@@ -841,23 +794,23 @@ codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
 }
 
 static void
-codegen_linux_x86_64_emit_if(codegen_x86_64_t *codegen, ast_if_stmt_t if_stmt)
+codegen_x86_64_emit_if(codegen_x86_64_t *codegen, ast_if_stmt_t if_stmt)
 {
     ast_node_t *cond = if_stmt.cond;
     ast_node_t *then = if_stmt.then;
     ast_node_t *_else = if_stmt._else;
 
-    size_t end_if_label = codegen_linux_x86_64_get_next_label(codegen);
-    size_t end_else_label = codegen_linux_x86_64_get_next_label(codegen);
+    size_t end_if_label = codegen_x86_64_get_next_label(codegen);
+    size_t end_else_label = codegen_x86_64_get_next_label(codegen);
 
-    codegen_linux_x86_64_emit_expression(codegen, cond);
+    codegen_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(codegen, &then_block);
+    codegen_x86_64_emit_block(codegen, &then_block);
     fprintf(codegen->out, "    jmp .L%ld\n", end_else_label);
 
     fprintf(codegen->out, ".L%ld:\n", end_if_label);
@@ -865,10 +818,10 @@ codegen_linux_x86_64_emit_if(codegen_x86_64_t *codegen, ast_if_stmt_t if_stmt)
     if (_else != NULL) {
         if (_else->kind == AST_NODE_IF_STMT) {
             ast_if_stmt_t else_if = _else->as_if_stmt;
-            codegen_linux_x86_64_emit_if(codegen, else_if);
+            codegen_x86_64_emit_if(codegen, else_if);
         } else {
             ast_block_t else_block = _else->as_block;
-            codegen_linux_x86_64_emit_block(codegen, &else_block);
+            codegen_x86_64_emit_block(codegen, &else_block);
         }
     }
 
@@ -931,8 +884,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_def)
+codegen_x86_64_emit_function(codegen_x86_64_t *codegen,
+                             ast_fn_definition_t *fn_def)
 {
     fprintf(codegen->out, ".globl " SV_FMT "\n", SV_ARG(fn_def->id));
     codegen->base_offset = 0;
@@ -957,8 +910,7 @@ codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen,
         codegen->base_offset += 8;
         size_t offset = codegen->base_offset;
 
-        codegen_linux_x86_64_put_stack_offset(
-            codegen, symbol, codegen->base_offset);
+        codegen_x86_64_put_stack_offset(codegen, symbol, codegen->base_offset);
 
         fprintf(codegen->out,
                 "    mov %s, -%ld(%%rbp)\n",
@@ -978,13 +930,13 @@ codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen,
     assert(block_node->kind == AST_NODE_BLOCK);
     ast_block_t block = block_node->as_block;
 
-    codegen_linux_x86_64_emit_block(codegen, &block);
+    codegen_x86_64_emit_block(codegen, &block);
 }
 
 static void
-codegen_linux_x86_64_put_stack_offset(codegen_x86_64_t *codegen,
-                                      symbol_t *symbol,
-                                      size_t offset)
+codegen_x86_64_put_stack_offset(codegen_x86_64_t *codegen,
+                                symbol_t *symbol,
+                                size_t offset)
 {
 
     size_t *stack_offset = arena_alloc(codegen->arena, sizeof(size_t));
@@ -997,8 +949,7 @@ codegen_linux_x86_64_put_stack_offset(codegen_x86_64_t *codegen,
 }
 
 static size_t
-codegen_linux_x86_64_get_stack_offset(codegen_x86_64_t *codegen,
-                                      symbol_t *symbol)
+codegen_x86_64_get_stack_offset(codegen_x86_64_t *codegen, symbol_t *symbol)
 {
     char symbol_ptr[PTR_HEX_CSTR_SIZE];
     sprintf(symbol_ptr, "%lx", (uintptr_t)symbol);
diff --git a/src/codegen_linux_x86_64.h b/src/codegen_x86_64.h
similarity index 82%
rename from src/codegen_linux_x86_64.h
rename to src/codegen_x86_64.h
index 7e2841d..d4948e1 100644
--- a/src/codegen_linux_x86_64.h
+++ b/src/codegen_x86_64.h
@@ -32,10 +32,10 @@ typedef struct codegen_x86_64
 } codegen_x86_64_t;
 
 void
-codegen_linux_x86_64_init(codegen_x86_64_t *codegen, arena_t *arena, FILE *out);
+codegen_x86_64_init(codegen_x86_64_t *codegen, arena_t *arena, FILE *out);
 
 void
-codegen_linux_x86_64_emit_translation_unit(codegen_x86_64_t *codegen,
-                                           ast_node_t *prog);
+codegen_x86_64_emit_translation_unit(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 49b2240..685d8ce 100644
--- a/src/main.c
+++ b/src/main.c
@@ -24,8 +24,8 @@
 #include "arena.h"
 #include "checker.h"
 #include "cli.h"
-#include "codegen_linux_aarch64.h"
-#include "codegen_linux_x86_64.h"
+#include "codegen_aarch64.h"
+#include "codegen_x86_64.h"
 #include "lexer.h"
 #include "parser.h"
 #include "pretty_print_ast.h"
@@ -148,15 +148,15 @@ handle_codegen_linux(cli_opts_t *opts)
 
     if (!(opts->options & CLI_OPT_ARCH)) {
         codegen_x86_64_t codegen = { 0 };
-        codegen_linux_x86_64_init(&codegen, &arena, out);
-        codegen_linux_x86_64_emit_translation_unit(&codegen, ast);
+        codegen_x86_64_init(&codegen, &arena, out);
+        codegen_x86_64_emit_translation_unit(&codegen, ast);
     } else {
         if (strcmp(opts->arch, "x86_64") == 0) {
             codegen_x86_64_t codegen = { 0 };
-            codegen_linux_x86_64_init(&codegen, &arena, out);
-            codegen_linux_x86_64_emit_translation_unit(&codegen, ast);
+            codegen_x86_64_init(&codegen, &arena, out);
+            codegen_x86_64_emit_translation_unit(&codegen, ast);
         } else if (strcmp(opts->arch, "aarch64") == 0) {
-            codegen_linux_aarch64_emit_translation_unit(out, ast);
+            codegen_aarch64_emit_translation_unit(out, ast);
         } else {
             fprintf(
                 stderr, "error: architecture '%s' not supported\n", opts->arch);
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1] codegen: x64: use the low bit register for 8 bits ops
@ 2024-10-17  0:16 Carlos Maniero
  2024-10-17  0:17 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-17  0:16 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

There was some registers that was using the high bit instead.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 src/codegen_x86_64.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/codegen_x86_64.c b/src/codegen_x86_64.c
index 1951365..c4cef3c 100644
--- a/src/codegen_x86_64.c
+++ b/src/codegen_x86_64.c
@@ -967,7 +967,7 @@ get_reg_for(x86_64_register_type_t type, size_t bytes)
     switch (type) {
         case REG_ACCUMULATOR: {
             if (bytes <= 1) {
-                return "%ah";
+                return "%al";
             } else if (bytes <= 2) {
                 return "%ax";
             } else if (bytes <= 4) {
@@ -977,7 +977,7 @@ get_reg_for(x86_64_register_type_t type, size_t bytes)
         }
         case REG_BASE: {
             if (bytes <= 1) {
-                return "%bh";
+                return "%bl";
             } else if (bytes <= 2) {
                 return "%bx";
             } else if (bytes <= 4) {
@@ -987,7 +987,7 @@ get_reg_for(x86_64_register_type_t type, size_t bytes)
         }
         case REG_COUNTER: {
             if (bytes <= 1) {
-                return "%ch";
+                return "%cl";
             } else if (bytes <= 2) {
                 return "%cx";
             } else if (bytes <= 4) {
@@ -997,7 +997,7 @@ get_reg_for(x86_64_register_type_t type, size_t bytes)
         }
         case REG_DATA: {
             if (bytes <= 1) {
-                return "%dh";
+                return "%dl";
             } else if (bytes <= 2) {
                 return "%dx";
             } else if (bytes <= 4) {

base-commit: c01fa7cf444bdd2cc9d6befff462285783263413
-- 
2.46.1


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v2] docs: info: add while-loop to getting started
@ 2024-10-16 21:43 Johnny Richard
  2024-10-16 19:44 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Johnny Richard @ 2024-10-16 21:43 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

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

diff --git a/docs/info/getting-started.texi b/docs/info/getting-started.texi
index a68f5b1..42b1438 100644
--- a/docs/info/getting-started.texi
+++ b/docs/info/getting-started.texi
@@ -68,6 +68,8 @@ var answer: u32 = 42
 
 Any non zero expr is true.
 
+@subsection If-Else
+
 @verbatim
 if expr {
   # statement
@@ -78,6 +80,14 @@ if expr {
 }
 @end verbatim
 
+@subsection While loop
+
+@verbatim
+while expr {
+  # statement
+}
+@end verbatim
+
 @section Primitive data types
 
 @table @samp

base-commit: cf5e4abf07a38f0ddf3ac6979b01b942ab99a691
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v2] spec: remove comments from the spec
@ 2024-10-16 19:41 Carlos Maniero
  2024-10-16 19:41 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-16 19:41 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

The comments bellow were now allowed following our spec:

{
  # this is now allowed
  return 1 # so is this
}

To avoid spreading the comment all over our spec, the comment is no
longer part of the spec and we are get riding of anything that starts
with #.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 contrib/bin/run-ebnf | 2 +-
 docs/info/olang.ebnf | 3 +--
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/contrib/bin/run-ebnf b/contrib/bin/run-ebnf
index 4b2b559..85b7c35 100755
--- a/contrib/bin/run-ebnf
+++ b/contrib/bin/run-ebnf
@@ -19,4 +19,4 @@ BASEDIR="$(dirname "$0")"
 
 DEPS='{:deps {instaparse/instaparse {:mvn/version "1.5.0"}}}'
 
-cat $BASEDIR/../../docs/info/olang.ebnf | sed 's/<\([a-z-]*\)>/\1/g' | FILE="$1" clj -Sdeps "$DEPS" -M $BASEDIR/ebnf.clj
+cat $BASEDIR/../../docs/info/olang.ebnf | sed 's/<\([a-z-]*\)>/\1/g' | FILE=<(cat $1 | sed 's/#.*$//') clj -Sdeps "$DEPS" -M $BASEDIR/ebnf.clj
diff --git a/docs/info/olang.ebnf b/docs/info/olang.ebnf
index 791c6f0..0bc7eb5 100644
--- a/docs/info/olang.ebnf
+++ b/docs/info/olang.ebnf
@@ -1,5 +1,5 @@
 (* Entry Point *)
-<translation-unit> ::= (<ows> (<external-declaration> | <comment>) <ows> (<end-of-statement> | <end-of-file>))*
+<translation-unit> ::= (<ows> (<external-declaration>) <ows> (<end-of-statement> | <end-of-file>))*
 
 (* Translation Unit *)
 <external-declaration> ::= <common-statement> | <function-definition>
@@ -76,7 +76,6 @@
 <integer-base16>  ::= #'0[Xx]' <hex-digit> (<hex-digit> | '_')*
 
 (* Utilities *)
-<comment>      ::= '#' #'[^\n]*'
 <ws>           ::= <white-space>+
 <ows>          ::= <white-space>*
 <white-space>  ::= <linear-space> | <line-break>
-- 
2.46.1


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang] spec: allow comments inside blocks
@ 2024-10-15 22:59 Carlos Maniero
  2024-10-15 22:59 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-15 22:59 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

The comments bellow were now allowed following our spec:

{
  # this is now allowed
  return 1 # so is this
}

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 docs/info/olang.ebnf | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/docs/info/olang.ebnf b/docs/info/olang.ebnf
index 791c6f0..475be6e 100644
--- a/docs/info/olang.ebnf
+++ b/docs/info/olang.ebnf
@@ -16,10 +16,10 @@
 <function-params>     ::= <identifier> <ows> ':' <ows> <type> ( <ows> ',' <ows> <function-params>)*
 <return-type>         ::= <type>
 <function-body>       ::= <block>
-<block>               ::= '{' <ows> <statement> <ows> (<end-of-statement> <ows> <statement> <ows>)* <end-of-statement>? <ows> '}'
+<block>               ::= '{' <ows> <statement> <ows> <comment>? (<end-of-statement> <ows> <statement> <ows>)* <end-of-statement>? <ows> '}'
 <function-args>       ::= <expression> (<ows> ',' <ows> <function-args>)*
 <function-call>       ::= <function-name> <ows> '(' ( <ows> | <ows> <function-args> <ows> ) ')'
-<statement>           ::= <common-statement> | <if-statement> | <while-statement> | <return-statement> | <function-call>
+<statement>           ::= <common-statement> | <if-statement> | <while-statement> | <return-statement> | <function-call> | <comment>
 <if-statement>        ::= 'if' <ws> <expression> <ows> <block> ( <ows> 'else' ( <ows> <block> | <ows> <if-statement> ) )?
 <while-statement>     ::= 'while' <ws> <expression> <ows> <block>
 <return-statement>    ::= 'return' <ws> <expression>
-- 
2.46.1


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1 3/3] codegen: x64: generate dereference assignment
@ 2024-10-14 12:06 Carlos Maniero
  2024-10-14 12:07 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-14 12:06 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 src/codegen_linux_x86_64.c | 73 +++++++++++++++++++++++++++++---------
 tests/olc/0034_pointers.ol |  4 +--
 2 files changed, 58 insertions(+), 19 deletions(-)

diff --git a/src/codegen_linux_x86_64.c b/src/codegen_linux_x86_64.c
index 153c25d..fc8fcc4 100644
--- a/src/codegen_linux_x86_64.c
+++ b/src/codegen_linux_x86_64.c
@@ -657,23 +657,54 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
                     return 1;
                 }
                 case AST_BINOP_ASSIGN: {
-                    // FIXME: It may not be a ref
-                    ast_ref_t ref = bin_op.lhs->as_ref;
-                    scope_t *scope = ref.scope;
-
-                    symbol_t *symbol = scope_lookup(scope, ref.id);
-                    assert(symbol);
-
-                    size_t offset =
-                        codegen_linux_x86_64_get_stack_offset(codegen, symbol);
-
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
-
-                    size_t type_size = type_to_bytes(symbol->type);
-                    fprintf(codegen->out,
-                            "    mov %s, -%ld(%%rbp)\n",
-                            get_reg_for(REG_ACCUMULATOR, type_size),
-                            offset);
+                    switch (bin_op.lhs->kind) {
+                        case AST_NODE_REF: {
+                            ast_ref_t ref = bin_op.lhs->as_ref;
+                            scope_t *scope = ref.scope;
+
+                            symbol_t *symbol = scope_lookup(scope, ref.id);
+                            assert(symbol);
+
+                            size_t offset =
+                                codegen_linux_x86_64_get_stack_offset(codegen,
+                                                                      symbol);
+
+                            codegen_linux_x86_64_emit_expression(codegen,
+                                                                 bin_op.rhs);
+
+                            size_t type_size = type_to_bytes(symbol->type);
+                            fprintf(codegen->out,
+                                    "    mov %s, -%ld(%%rbp)\n",
+                                    get_reg_for(REG_ACCUMULATOR, type_size),
+                                    offset);
+                            break;
+                        }
+                        case AST_NODE_UNARY_OP: {
+                            assert(bin_op.lhs->as_unary_op.kind ==
+                                       AST_UNARY_DEREFERENCE &&
+                                   "unsupported assignment lhs");
+
+                            codegen_linux_x86_64_emit_expression(codegen,
+                                                                 bin_op.lhs);
+
+                            fprintf(codegen->out, "    push %%rax\n");
+
+                            size_t type_size =
+                                codegen_linux_x86_64_emit_expression(
+                                    codegen, bin_op.rhs);
+
+                            fprintf(codegen->out, "    pop %%rdx\n");
+
+                            fprintf(codegen->out,
+                                    "    mov %s, (%%rdx) \n",
+                                    get_reg_for(REG_ACCUMULATOR, type_size));
+
+                            break;
+                        }
+                        default: {
+                            assert(false && "unsupported assignment lhs");
+                        }
+                    }
 
                     // FIXME: we don't support a = b = c
                     return 0;
@@ -715,6 +746,14 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen,
                         codegen->out, "    lea -%ld(%%rbp), %%rax\n", offset);
                     return 8;
                 }
+                case AST_UNARY_DEREFERENCE: {
+                    // FIXME: support dereference of dereference (**)
+                    assert(unary_op.expr->kind == AST_NODE_REF &&
+                           "unsupported unary expression for dereference (*)");
+
+                    return codegen_linux_x86_64_emit_expression(codegen,
+                                                                unary_op.expr);
+                }
                 default: {
                     assert(0 && "unsupported unary operation");
                     return 0;
diff --git a/tests/olc/0034_pointers.ol b/tests/olc/0034_pointers.ol
index 0e95af4..b3a693b 100644
--- a/tests/olc/0034_pointers.ol
+++ b/tests/olc/0034_pointers.ol
@@ -20,9 +20,9 @@ fn main(): u32 {
   return a
 }
 
-# xTEST test_compile(exit_code=0)
+# TEST test_compile(exit_code=0)
 #
-# xTEST test_run_binary(exit_code=0)
+# TEST test_run_binary(exit_code=0)
 #
 # TEST test_ast WITH
 # Translation_Unit
-- 
2.46.1


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1 2/2] parser: spec: allow nested unary expressions
@ 2024-10-14 11:29 Johnny Richard
  2024-10-14  9:31 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Johnny Richard @ 2024-10-14 11:29 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 docs/info/olang.ebnf           | 13 ++++----
 src/parser.c                   | 58 ++++++++++++++++++++++++----------
 tests/olc/0035_unary_nested.ol | 36 +++++++++++++++++++++
 3 files changed, 84 insertions(+), 23 deletions(-)
 create mode 100644 tests/olc/0035_unary_nested.ol

diff --git a/docs/info/olang.ebnf b/docs/info/olang.ebnf
index 76a5c2f..791c6f0 100644
--- a/docs/info/olang.ebnf
+++ b/docs/info/olang.ebnf
@@ -52,19 +52,18 @@
 <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>)*
-<primary-expression>        ::= <integer-literal>
-                              | <variable-name>
-                              | <function-call>
-                              | <unary-expression>
-                              | '(' <ows> <expression> <ows> ')'
-<unary-expression>          ::= <unary-operator> <ows> <primary-expression>
+<multiplicative-expression> ::= <unary-expression> (<ows> ('*' | '/' | '%') <ows> <unary-expression>)*
+<unary-expression>          ::= (<unary-operator> <ows>)* <primary-expression>
 <unary-operator>            ::= '&'
                               | '*'
                               | '+'
                               | '-'
                               | '~'
                               | '!'
+<primary-expression>        ::= <integer-literal>
+                              | <variable-name>
+                              | <function-call>
+                              | '(' <ows> <expression> <ows> ')'
 
 (* Identifiers *)
 <type>       ::= ('u8' | 'u16' | 'u32' | 'u64') (<ows> '*')*
diff --git a/src/parser.c b/src/parser.c
index 8b103df..35cf017 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -28,6 +28,9 @@
 static bool
 skip_expected_token(parser_t *parser, token_kind_t expected_kind);
 
+static void
+skip_next_token(parser_t *parser);
+
 static bool
 expected_next_token(parser_t *parser, token_t *token, token_kind_t kind);
 
@@ -64,6 +67,9 @@ parser_parse_fn_params(parser_t *parser);
 static ast_node_t *
 parser_parse_expr(parser_t *parser);
 
+static ast_node_t *
+parser_parse_unary_expr(parser_t *parser);
+
 static ast_node_t *
 parser_parse_primary_expr(parser_t *parser);
 
@@ -247,7 +253,7 @@ parser_parse_expr_1(parser_t *parser, ast_node_t *lhs, size_t prev_precedence)
         token_t token_op;
         lexer_next_token(parser->lexer, &token_op);
 
-        ast_node_t *rhs = parser_parse_primary_expr(parser);
+        ast_node_t *rhs = parser_parse_unary_expr(parser);
         if (rhs == NULL) {
             return NULL;
         }
@@ -278,7 +284,7 @@ parser_parse_expr_1(parser_t *parser, ast_node_t *lhs, size_t prev_precedence)
 static ast_node_t *
 parser_parse_expr(parser_t *parser)
 {
-    ast_node_t *lhs = parser_parse_primary_expr(parser);
+    ast_node_t *lhs = parser_parse_unary_expr(parser);
     if (lhs == NULL) {
         return NULL;
     }
@@ -286,6 +292,33 @@ parser_parse_expr(parser_t *parser)
     return parser_parse_expr_1(parser, lhs, BINOP_MIN_PREC);
 }
 
+static ast_node_t *
+parser_parse_unary_expr(parser_t *parser)
+{
+    token_t token;
+    lexer_peek_next(parser->lexer, &token);
+    switch (token.kind) {
+        case TOKEN_AND:
+        case TOKEN_STAR:
+        case TOKEN_PLUS:
+        case TOKEN_DASH:
+        case TOKEN_TILDE:
+        case TOKEN_BANG: {
+            skip_next_token(parser);
+            ast_node_t *expr = parser_parse_unary_expr(parser);
+            if (expr == NULL) {
+                return NULL;
+            }
+
+            ast_unary_op_kind_t kind = token_kind_to_unary_op_kind(token.kind);
+            return ast_new_node_unary_op(parser->arena, token.loc, kind, expr);
+        }
+        default: {
+            return parser_parse_primary_expr(parser);
+        }
+    }
+}
+
 static ast_node_t *
 parser_parse_primary_expr(parser_t *parser)
 {
@@ -310,20 +343,6 @@ parser_parse_primary_expr(parser_t *parser)
             return ast_new_node_ref(
                 parser->arena, token_id.loc, token_id.value);
         }
-        case TOKEN_AND:
-        case TOKEN_STAR:
-        case TOKEN_PLUS:
-        case TOKEN_DASH:
-        case TOKEN_TILDE:
-        case TOKEN_BANG: {
-            ast_node_t *expr = parser_parse_primary_expr(parser);
-            if (expr == NULL) {
-                return NULL;
-            }
-
-            ast_unary_op_kind_t kind = token_kind_to_unary_op_kind(token.kind);
-            return ast_new_node_unary_op(parser->arena, token.loc, kind, expr);
-        }
 
         case TOKEN_OPAREN: {
             ast_node_t *expr = parser_parse_expr(parser);
@@ -740,6 +759,13 @@ skip_expected_token(parser_t *parser, token_kind_t expected_kind)
     return expected_next_token(parser, &token, expected_kind);
 }
 
+static void
+skip_next_token(parser_t *parser)
+{
+    token_t token;
+    lexer_next_token(parser->lexer, &token);
+}
+
 static bool
 expected_next_token(parser_t *parser,
                     token_t *token,
diff --git a/tests/olc/0035_unary_nested.ol b/tests/olc/0035_unary_nested.ol
new file mode 100644
index 0000000..ef4e936
--- /dev/null
+++ b/tests/olc/0035_unary_nested.ol
@@ -0,0 +1,36 @@
+# 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 e: u32 = 0 
+  return ~~e
+}
+
+# TEST test_compile(exit_code=0)
+#
+# TEST test_run_binary(exit_code=0)
+#
+# TEST test_ast WITH
+# Translation_Unit
+# `-Function_Definition <name:main> <return:u32>
+#   `-Block
+#     |-Var_Definition <name:e> <kind:u32>
+#     | `-Literal <kind:u32> <value:0>
+#     `-Return_Statement
+#       `-Unary_Operation (~)
+#         `-Unary_Operation (~)
+#           `-Reference <name:e>
+# END
+
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v2 4/4] codestyle: limit the code to 80 characters
@ 2024-10-11 16:57 Carlos Maniero
  2024-10-11 16:58 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-11 16:57 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 .clang-format                 |   4 +-
 src/ast.c                     |  60 ++++--
 src/ast.h                     |  38 +++-
 src/checker.c                 |  13 +-
 src/cli.c                     |  29 +--
 src/codegen_linux_x86_64.c    | 339 ++++++++++++++++++++++++----------
 src/codegen_linux_x86_64.h    |   3 +-
 src/lexer.c                   |  46 +++--
 src/main.c                    |  19 +-
 src/map.c                     |  12 +-
 src/parser.c                  |  73 ++++++--
 src/pretty_print_ast.c        |  81 +++++---
 src/scope.c                   |   9 +-
 tests/unit/arena_test.c       |  26 ++-
 tests/unit/list_test.c        |  33 +++-
 tests/unit/map_test.c         |  20 +-
 tests/unit/string_view_test.c |  26 ++-
 17 files changed, 608 insertions(+), 223 deletions(-)

diff --git a/.clang-format b/.clang-format
index fa8dbc8..f7c0cc7 100644
--- a/.clang-format
+++ b/.clang-format
@@ -77,7 +77,7 @@ BreakConstructorInitializersBeforeComma: false
 BreakConstructorInitializers: BeforeComma
 BreakAfterJavaFieldAnnotations: false
 BreakStringLiterals: true
-ColumnLimit:     120
+ColumnLimit:     80
 CommentPragmas:  '^ IWYU pragma:'
 QualifierAlignment: Leave
 CompactNamespaces: false
@@ -145,7 +145,7 @@ ObjCSpaceBeforeProtocolList: false
 PenaltyBreakAssignment: 2
 PenaltyBreakBeforeFirstCallParameter: 19
 PenaltyBreakComment: 300
-PenaltyBreakFirstLessLess: 120
+PenaltyBreakFirstLessLess: 80
 PenaltyBreakOpenParenthesis: 0
 PenaltyBreakString: 1000
 PenaltyBreakTemplateDeclaration: 10
diff --git a/src/ast.c b/src/ast.c
index c64e660..800239e 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -51,7 +51,8 @@ ast_new_node_fn_def(arena_t *arena,
     assert(params);
     assert(block);
 
-    ast_node_t *node_fn_def = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
+    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;
@@ -67,12 +68,16 @@ ast_new_node_fn_def(arena_t *arena,
 }
 
 ast_node_t *
-ast_new_node_fn_call(arena_t *arena, token_loc_t loc, string_view_t id, list_t *args)
+ast_new_node_fn_call(arena_t *arena,
+                     token_loc_t loc,
+                     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));
+    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;
@@ -86,9 +91,14 @@ ast_new_node_fn_call(arena_t *arena, token_loc_t loc, string_view_t id, list_t *
 }
 
 ast_node_t *
-ast_new_node_var_def(arena_t *arena, token_loc_t loc, string_view_t id, type_t *type, ast_node_t *value)
+ast_new_node_var_def(arena_t *arena,
+                     token_loc_t loc,
+                     string_view_t id,
+                     type_t *type,
+                     ast_node_t *value)
 {
-    ast_node_t *node_var_def = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
+    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;
@@ -103,9 +113,14 @@ ast_new_node_var_def(arena_t *arena, token_loc_t loc, string_view_t id, type_t *
 }
 
 ast_node_t *
-ast_new_node_bin_op(arena_t *arena, token_loc_t loc, ast_binary_op_kind_t kind, ast_node_t *lhs, ast_node_t *rhs)
+ast_new_node_bin_op(arena_t *arena,
+                    token_loc_t loc,
+                    ast_binary_op_kind_t kind,
+                    ast_node_t *lhs,
+                    ast_node_t *rhs)
 {
-    ast_node_t *node_bin_op = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
+    ast_node_t *node_bin_op =
+        (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
     assert(node_bin_op);
 
     node_bin_op->kind = AST_NODE_BINARY_OP;
@@ -118,9 +133,13 @@ ast_new_node_bin_op(arena_t *arena, token_loc_t loc, ast_binary_op_kind_t kind,
 }
 
 ast_node_t *
-ast_new_node_unary_op(arena_t *arena, token_loc_t loc, ast_unary_op_kind_t kind, ast_node_t *expr)
+ast_new_node_unary_op(arena_t *arena,
+                      token_loc_t loc,
+                      ast_unary_op_kind_t kind,
+                      ast_node_t *expr)
 {
-    ast_node_t *node_unary_op = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
+    ast_node_t *node_unary_op =
+        (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
     assert(node_unary_op);
 
     node_unary_op->kind = AST_NODE_UNARY_OP;
@@ -134,7 +153,8 @@ ast_new_node_unary_op(arena_t *arena, token_loc_t loc, ast_unary_op_kind_t kind,
 ast_node_t *
 ast_new_node_literal_u32(arena_t *arena, token_loc_t loc, uint32_t value)
 {
-    ast_node_t *node_literal = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
+    ast_node_t *node_literal =
+        (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
     assert(node_literal);
 
     node_literal->kind = AST_NODE_LITERAL;
@@ -161,7 +181,8 @@ ast_new_node_ref(arena_t *arena, token_loc_t loc, string_view_t id)
 ast_node_t *
 ast_new_node_return_stmt(arena_t *arena, token_loc_t loc, ast_node_t *expr)
 {
-    ast_node_t *node_return_stmt = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
+    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;
@@ -172,7 +193,11 @@ ast_new_node_return_stmt(arena_t *arena, token_loc_t loc, ast_node_t *expr)
 }
 
 ast_node_t *
-ast_new_node_if_stmt(arena_t *arena, token_loc_t loc, ast_node_t *cond, ast_node_t *then, ast_node_t *_else)
+ast_new_node_if_stmt(arena_t *arena,
+                     token_loc_t loc,
+                     ast_node_t *cond,
+                     ast_node_t *then,
+                     ast_node_t *_else)
 {
     ast_node_t *node_if_stmt = arena_alloc(arena, sizeof(ast_node_t));
     assert(node_if_stmt);
@@ -187,7 +212,10 @@ ast_new_node_if_stmt(arena_t *arena, token_loc_t loc, ast_node_t *cond, ast_node
 }
 
 ast_node_t *
-ast_new_node_while_stmt(arena_t *arena, token_loc_t loc, ast_node_t *cond, ast_node_t *then)
+ast_new_node_while_stmt(arena_t *arena,
+                        token_loc_t loc,
+                        ast_node_t *cond,
+                        ast_node_t *then)
 {
     ast_node_t *node_while_stmt = arena_alloc(arena, sizeof(ast_node_t));
     assert(node_while_stmt);
@@ -203,7 +231,8 @@ ast_new_node_while_stmt(arena_t *arena, token_loc_t loc, ast_node_t *cond, ast_n
 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));
+    ast_node_t *node_block =
+        (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
     assert(node_block);
 
     node_block->kind = AST_NODE_BLOCK;
@@ -219,7 +248,8 @@ ast_new_node_block(arena_t *arena)
 ast_fn_param_t *
 ast_new_fn_param(arena_t *arena, string_view_t id, type_t *type)
 {
-    ast_fn_param_t *fn_param = (ast_fn_param_t *)arena_alloc(arena, sizeof(ast_fn_param_t));
+    ast_fn_param_t *fn_param =
+        (ast_fn_param_t *)arena_alloc(arena, sizeof(ast_fn_param_t));
     assert(fn_param);
 
     fn_param->id = id;
diff --git a/src/ast.h b/src/ast.h
index 6da11cf..217a264 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -229,16 +229,30 @@ ast_new_node_fn_def(arena_t *arena,
                     ast_node_t *block);
 
 ast_node_t *
-ast_new_node_fn_call(arena_t *arena, token_loc_t loc, string_view_t id, list_t *args);
+ast_new_node_fn_call(arena_t *arena,
+                     token_loc_t loc,
+                     string_view_t id,
+                     list_t *args);
 
 ast_node_t *
-ast_new_node_var_def(arena_t *arena, token_loc_t loc, string_view_t id, type_t *type, ast_node_t *value);
+ast_new_node_var_def(arena_t *arena,
+                     token_loc_t loc,
+                     string_view_t id,
+                     type_t *type,
+                     ast_node_t *value);
 
 ast_node_t *
-ast_new_node_bin_op(arena_t *arena, token_loc_t loc, ast_binary_op_kind_t kind, ast_node_t *lhs, ast_node_t *rhs);
+ast_new_node_bin_op(arena_t *arena,
+                    token_loc_t loc,
+                    ast_binary_op_kind_t kind,
+                    ast_node_t *lhs,
+                    ast_node_t *rhs);
 
 ast_node_t *
-ast_new_node_unary_op(arena_t *arena, token_loc_t loc, ast_unary_op_kind_t kind, ast_node_t *expr);
+ast_new_node_unary_op(arena_t *arena,
+                      token_loc_t loc,
+                      ast_unary_op_kind_t kind,
+                      ast_node_t *expr);
 
 ast_node_t *
 ast_new_node_literal_u32(arena_t *arena, token_loc_t loc, uint32_t value);
@@ -247,16 +261,26 @@ ast_node_t *
 ast_new_node_ref(arena_t *arena, token_loc_t loc, string_view_t id);
 
 ast_node_t *
-ast_new_node_var_assign_stmt(arena_t *arena, token_loc_t loc, ast_node_t *ref, ast_node_t *expr);
+ast_new_node_var_assign_stmt(arena_t *arena,
+                             token_loc_t loc,
+                             ast_node_t *ref,
+                             ast_node_t *expr);
 
 ast_node_t *
 ast_new_node_return_stmt(arena_t *arena, token_loc_t loc, ast_node_t *expr);
 
 ast_node_t *
-ast_new_node_if_stmt(arena_t *arena, token_loc_t loc, ast_node_t *cond, ast_node_t *then, ast_node_t *_else);
+ast_new_node_if_stmt(arena_t *arena,
+                     token_loc_t loc,
+                     ast_node_t *cond,
+                     ast_node_t *then,
+                     ast_node_t *_else);
 
 ast_node_t *
-ast_new_node_while_stmt(arena_t *arena, token_loc_t loc, ast_node_t *cond, ast_node_t *then);
+ast_new_node_while_stmt(arena_t *arena,
+                        token_loc_t loc,
+                        ast_node_t *cond,
+                        ast_node_t *then);
 
 ast_node_t *
 ast_new_node_block(arena_t *arena);
diff --git a/src/checker.c b/src/checker.c
index e06cfe9..1230c3c 100644
--- a/src/checker.c
+++ b/src/checker.c
@@ -30,7 +30,9 @@ checker_new(arena_t *arena)
 
     checker_t *checker = (checker_t *)arena_alloc(arena, sizeof(checker_t));
     if (checker == NULL) {
-        fprintf(stderr, "[FATAL] Out of memory: checker_new: %s\n", strerror(errno));
+        fprintf(stderr,
+                "[FATAL] Out of memory: checker_new: %s\n",
+                strerror(errno));
         exit(EXIT_FAILURE);
     }
     checker->arena = arena;
@@ -119,7 +121,8 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
             fn_def->scope = scope_push(scope);
 
             type_resolve(fn_def->return_type);
-            symbol_t *symbol = symbol_new(checker->arena, fn_def->id, fn_def->return_type);
+            symbol_t *symbol =
+                symbol_new(checker->arena, fn_def->id, fn_def->return_type);
             scope_insert(scope, symbol);
 
             list_item_t *item = list_head(fn_def->params);
@@ -128,7 +131,8 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
                 ast_fn_param_t *param = (ast_fn_param_t *)item->value;
 
                 type_resolve(param->type);
-                symbol_t *symbol = symbol_new(checker->arena, param->id, param->type);
+                symbol_t *symbol =
+                    symbol_new(checker->arena, param->id, param->type);
                 scope_insert(fn_def->scope, symbol);
 
                 item = list_next(item);
@@ -210,7 +214,8 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
 
             type_resolve(ast->as_var_def.type);
 
-            symbol_t *symbol = symbol_new(checker->arena, id, ast->as_var_def.type);
+            symbol_t *symbol =
+                symbol_new(checker->arena, id, ast->as_var_def.type);
 
             scope_insert(scope, symbol);
             ast->as_var_def.scope = scope;
diff --git a/src/cli.c b/src/cli.c
index d6b69e1..2f61acb 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -65,7 +65,8 @@ cli_parse_args(int argc, char **argv)
         arg = cli_args_shift(&args);
     }
 
-    if (opts.options & CLI_OPT_OUTPUT || opts.options & CLI_OPT_DUMP_TOKENS || opts.options & CLI_OPT_DUMP_AST) {
+    if (opts.options & CLI_OPT_OUTPUT || opts.options & CLI_OPT_DUMP_TOKENS ||
+        opts.options & CLI_OPT_DUMP_AST) {
         return opts;
     }
 
@@ -110,7 +111,9 @@ cli_opts_parse_arch(cli_opts_t *opts, cli_args_t *args)
     char *arch = cli_args_shift(args);
 
     if (arch == NULL) {
-        fprintf(stderr, "error: missing architecture for arg '--arch': available options (x86_64 | aarch64)\n");
+        fprintf(stderr,
+                "error: missing architecture for arg '--arch': available "
+                "options (x86_64 | aarch64)\n");
         cli_print_usage(stderr, opts->compiler_path);
         exit(EXIT_FAILURE);
     }
@@ -140,14 +143,16 @@ cli_opts_parse_sysroot(cli_opts_t *opts, cli_args_t *args)
 void
 cli_print_usage(FILE *stream, char *compiler_path)
 {
-    fprintf(stream,
-            "Usage: %s [options] file...\n"
-            "Options:\n"
-            "  --dump-tokens    Display lexer token stream\n"
-            "  --dump-ast       Display ast tree to stdout\n"
-            "  --arch <arch>    Binary arch: default to x86_64 (x86_64 | aarch64)\n"
-            "  --sysroot <dir>  System root dir where the GNU Assembler and GNU Linker are located: default to '/'\n"
-            "  -o <file>        Compile program into a binary file\n"
-            "  --save-temps     Keep temp files used to compile program\n",
-            compiler_path);
+    fprintf(
+        stream,
+        "Usage: %s [options] file...\n"
+        "Options:\n"
+        "  --dump-tokens    Display lexer token stream\n"
+        "  --dump-ast       Display ast tree to stdout\n"
+        "  --arch <arch>    Binary arch: default to x86_64 (x86_64 | aarch64)\n"
+        "  --sysroot <dir>  System root dir where the GNU Assembler and GNU "
+        "Linker are located: default to '/'\n"
+        "  -o <file>        Compile program into a binary file\n"
+        "  --save-temps     Keep temp files used to compile program\n",
+        compiler_path);
 }
diff --git a/src/codegen_linux_x86_64.c b/src/codegen_linux_x86_64.c
index 8a73263..053963a 100644
--- a/src/codegen_linux_x86_64.c
+++ b/src/codegen_linux_x86_64.c
@@ -58,22 +58,28 @@ typedef enum x86_64_register_type
  * ──────────────────────────────────────────────────────────────
  * x86-64        rdi   rsi   rdx   r10   r8    r9    -
  */
-static int x86_call_args[X86_CALL_ARG_SIZE] = { REG_DEST_IDX, REG_SRC_IDX, REG_DATA, REG_R10, REG_R8, REG_R9 };
+static int x86_call_args[X86_CALL_ARG_SIZE] = { REG_DEST_IDX, REG_SRC_IDX,
+                                                REG_DATA,     REG_R10,
+                                                REG_R8,       REG_R9 };
 
 static void
 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);
+codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen,
+                                   ast_fn_definition_t *fn);
 
 static void
 codegen_linux_x86_64_emit_if(codegen_x86_64_t *codegen, ast_if_stmt_t is_stmt);
 
 static void
-codegen_linux_x86_64_put_stack_offset(codegen_x86_64_t *codegen, symbol_t *symbol, size_t offset);
+codegen_linux_x86_64_put_stack_offset(codegen_x86_64_t *codegen,
+                                      symbol_t *symbol,
+                                      size_t offset);
 
 static size_t
-codegen_linux_x86_64_get_stack_offset(codegen_x86_64_t *codegen, symbol_t *symbol);
+codegen_linux_x86_64_get_stack_offset(codegen_x86_64_t *codegen,
+                                      symbol_t *symbol);
 
 static size_t
 type_to_bytes(type_t *type);
@@ -94,7 +100,8 @@ codegen_linux_x86_64_init(codegen_x86_64_t *codegen, arena_t *arena, FILE *out)
 }
 
 void
-codegen_linux_x86_64_emit_translation_unit(codegen_x86_64_t *codegen, ast_node_t *node)
+codegen_linux_x86_64_emit_translation_unit(codegen_x86_64_t *codegen,
+                                           ast_node_t *node)
 {
     codegen->label_index = 0;
     codegen_linux_x86_64_emit_start_entrypoint(codegen);
@@ -146,7 +153,8 @@ codegen_linux_x86_64_get_next_label(codegen_x86_64_t *codegen)
 typedef size_t size_in_bytes_t;
 
 static size_in_bytes_t
-codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, 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: {
@@ -163,11 +171,15 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
             symbol_t *symbol = scope_lookup(ref.scope, ref.id);
             assert(symbol);
 
-            size_t offset = codegen_linux_x86_64_get_stack_offset(codegen, symbol);
+            size_t offset =
+                codegen_linux_x86_64_get_stack_offset(codegen, symbol);
 
             size_t bytes = type_to_bytes(symbol->type);
 
-            fprintf(codegen->out, "    mov -%ld(%%rbp), %s\n", offset, get_reg_for(REG_ACCUMULATOR, bytes));
+            fprintf(codegen->out,
+                    "    mov -%ld(%%rbp), %s\n",
+                    offset,
+                    get_reg_for(REG_ACCUMULATOR, bytes));
             return bytes;
         }
         case AST_NODE_FN_CALL: {
@@ -177,7 +189,8 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
             assert(symbol);
 
             size_t i = 0;
-            for (list_item_t *item = list_head(fn_call.args); item != NULL; item = list_next(item)) {
+            for (list_item_t *item = list_head(fn_call.args); item != NULL;
+                 item = list_next(item)) {
                 // FIXME: add support for more args than X86_CALL_ARG_SIZE
                 assert(i < X86_CALL_ARG_SIZE);
 
@@ -185,12 +198,16 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
 
                 codegen_linux_x86_64_emit_expression(codegen, arg_node);
 
-                fprintf(codegen->out, "    push %s\n", get_reg_for(REG_ACCUMULATOR, 8));
+                fprintf(codegen->out,
+                        "    push %s\n",
+                        get_reg_for(REG_ACCUMULATOR, 8));
                 ++i;
             }
 
             for (; i > 0; --i) {
-                fprintf(codegen->out, "    pop %s\n", get_reg_for(x86_call_args[i - 1], 8));
+                fprintf(codegen->out,
+                        "    pop %s\n",
+                        get_reg_for(x86_call_args[i - 1], 8));
             }
 
             fprintf(codegen->out, "    call " SV_FMT "\n", SV_ARG(fn_call.id));
@@ -202,13 +219,18 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
             switch (bin_op.kind) {
                 case AST_BINOP_ADDITION: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    size_in_bytes_t rhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    size_in_bytes_t lhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.lhs);
 
-                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+                    size_in_bytes_t expr_bytes =
+                        bytes_max(rhs_bytes, lhs_bytes);
 
                     fprintf(codegen->out, "    pop %%rcx\n");
                     fprintf(codegen->out,
@@ -220,48 +242,69 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
                 }
                 case AST_BINOP_MULTIPLICATION: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    size_in_bytes_t rhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    size_in_bytes_t lhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.lhs);
 
-                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+                    size_in_bytes_t expr_bytes =
+                        bytes_max(rhs_bytes, lhs_bytes);
 
                     fprintf(codegen->out, "    pop %%rcx\n");
-                    fprintf(codegen->out, "    mul %s\n", get_reg_for(REG_COUNTER, expr_bytes));
+                    fprintf(codegen->out,
+                            "    mul %s\n",
+                            get_reg_for(REG_COUNTER, expr_bytes));
 
                     return expr_bytes;
                 }
                 case AST_BINOP_DIVISION: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    size_in_bytes_t rhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    size_in_bytes_t lhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.lhs);
 
-                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+                    size_in_bytes_t expr_bytes =
+                        bytes_max(rhs_bytes, lhs_bytes);
 
                     fprintf(codegen->out, "    pop %%rcx\n");
                     fprintf(codegen->out, "    xor %%rdx, %%rdx\n");
-                    fprintf(codegen->out, "    div %s\n", get_reg_for(REG_COUNTER, expr_bytes));
+                    fprintf(codegen->out,
+                            "    div %s\n",
+                            get_reg_for(REG_COUNTER, expr_bytes));
 
                     return expr_bytes;
                 }
                 case AST_BINOP_REMINDER: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    size_in_bytes_t rhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    size_in_bytes_t lhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.lhs);
 
-                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+                    size_in_bytes_t expr_bytes =
+                        bytes_max(rhs_bytes, lhs_bytes);
 
                     fprintf(codegen->out, "    pop %%rcx\n");
                     fprintf(codegen->out, "    xor %%rdx, %%rdx\n");
-                    fprintf(codegen->out, "    div %s\n", get_reg_for(REG_COUNTER, expr_bytes));
+                    fprintf(codegen->out,
+                            "    div %s\n",
+                            get_reg_for(REG_COUNTER, expr_bytes));
                     fprintf(codegen->out,
                             "    mov %s, %s\n",
                             get_reg_for(REG_DATA, expr_bytes),
@@ -271,13 +314,18 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
                 }
                 case AST_BINOP_SUBTRACTION: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    size_in_bytes_t rhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    size_in_bytes_t lhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.lhs);
 
-                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+                    size_in_bytes_t expr_bytes =
+                        bytes_max(rhs_bytes, lhs_bytes);
 
                     fprintf(codegen->out, "    pop %%rcx\n");
                     fprintf(codegen->out,
@@ -289,13 +337,18 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
                 }
                 case AST_BINOP_CMP_EQ: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    size_in_bytes_t rhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    size_in_bytes_t lhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.lhs);
 
-                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+                    size_in_bytes_t expr_bytes =
+                        bytes_max(rhs_bytes, lhs_bytes);
 
                     fprintf(codegen->out, "    pop %%rcx\n");
                     fprintf(codegen->out,
@@ -303,19 +356,26 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
                             get_reg_for(REG_COUNTER, expr_bytes),
                             get_reg_for(REG_ACCUMULATOR, expr_bytes));
                     fprintf(codegen->out, "    sete %%al\n");
-                    fprintf(codegen->out, "    movzb %%al, %s\n", get_reg_for(REG_ACCUMULATOR, expr_bytes));
+                    fprintf(codegen->out,
+                            "    movzb %%al, %s\n",
+                            get_reg_for(REG_ACCUMULATOR, expr_bytes));
 
                     return expr_bytes;
                 }
                 case AST_BINOP_CMP_LT: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    size_in_bytes_t rhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    size_in_bytes_t lhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.lhs);
 
-                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+                    size_in_bytes_t expr_bytes =
+                        bytes_max(rhs_bytes, lhs_bytes);
 
                     fprintf(codegen->out, "    pop %%rcx\n");
                     fprintf(codegen->out,
@@ -323,19 +383,26 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
                             get_reg_for(REG_COUNTER, expr_bytes),
                             get_reg_for(REG_ACCUMULATOR, expr_bytes));
                     fprintf(codegen->out, "    setl %%al\n");
-                    fprintf(codegen->out, "    movzb %%al, %s\n", get_reg_for(REG_ACCUMULATOR, expr_bytes));
+                    fprintf(codegen->out,
+                            "    movzb %%al, %s\n",
+                            get_reg_for(REG_ACCUMULATOR, expr_bytes));
 
                     return expr_bytes;
                 }
                 case AST_BINOP_CMP_GT: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    size_in_bytes_t rhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    size_in_bytes_t lhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.lhs);
 
-                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+                    size_in_bytes_t expr_bytes =
+                        bytes_max(rhs_bytes, lhs_bytes);
 
                     fprintf(codegen->out, "    pop %%rcx\n");
                     fprintf(codegen->out,
@@ -343,19 +410,26 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
                             get_reg_for(REG_COUNTER, expr_bytes),
                             get_reg_for(REG_ACCUMULATOR, expr_bytes));
                     fprintf(codegen->out, "    setg %%al\n");
-                    fprintf(codegen->out, "    movzb %%al, %s\n", get_reg_for(REG_ACCUMULATOR, expr_bytes));
+                    fprintf(codegen->out,
+                            "    movzb %%al, %s\n",
+                            get_reg_for(REG_ACCUMULATOR, expr_bytes));
 
                     return expr_bytes;
                 }
                 case AST_BINOP_CMP_NEQ: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    size_in_bytes_t rhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    size_in_bytes_t lhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.lhs);
 
-                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+                    size_in_bytes_t expr_bytes =
+                        bytes_max(rhs_bytes, lhs_bytes);
 
                     fprintf(codegen->out, "    pop %%rcx\n");
                     fprintf(codegen->out,
@@ -363,19 +437,26 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
                             get_reg_for(REG_COUNTER, expr_bytes),
                             get_reg_for(REG_ACCUMULATOR, expr_bytes));
                     fprintf(codegen->out, "    setne %%al\n");
-                    fprintf(codegen->out, "    movzb %%al, %s\n", get_reg_for(REG_ACCUMULATOR, expr_bytes));
+                    fprintf(codegen->out,
+                            "    movzb %%al, %s\n",
+                            get_reg_for(REG_ACCUMULATOR, expr_bytes));
 
                     return expr_bytes;
                 }
                 case AST_BINOP_CMP_LEQ: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    size_in_bytes_t rhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    size_in_bytes_t lhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.lhs);
 
-                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+                    size_in_bytes_t expr_bytes =
+                        bytes_max(rhs_bytes, lhs_bytes);
 
                     fprintf(codegen->out, "    pop %%rcx\n");
                     fprintf(codegen->out,
@@ -383,19 +464,26 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
                             get_reg_for(REG_COUNTER, expr_bytes),
                             get_reg_for(REG_ACCUMULATOR, expr_bytes));
                     fprintf(codegen->out, "    setle %%al\n");
-                    fprintf(codegen->out, "    movzb %%al, %s\n", get_reg_for(REG_ACCUMULATOR, expr_bytes));
+                    fprintf(codegen->out,
+                            "    movzb %%al, %s\n",
+                            get_reg_for(REG_ACCUMULATOR, expr_bytes));
 
                     return expr_bytes;
                 }
                 case AST_BINOP_CMP_GEQ: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    size_in_bytes_t rhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    size_in_bytes_t lhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.lhs);
 
-                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+                    size_in_bytes_t expr_bytes =
+                        bytes_max(rhs_bytes, lhs_bytes);
 
                     fprintf(codegen->out, "    pop %%rcx\n");
                     fprintf(codegen->out,
@@ -403,7 +491,9 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
                             get_reg_for(REG_COUNTER, expr_bytes),
                             get_reg_for(REG_ACCUMULATOR, expr_bytes));
                     fprintf(codegen->out, "    setge %%al\n");
-                    fprintf(codegen->out, "    movzb %%al, %s\n", get_reg_for(REG_ACCUMULATOR, expr_bytes));
+                    fprintf(codegen->out,
+                            "    movzb %%al, %s\n",
+                            get_reg_for(REG_ACCUMULATOR, expr_bytes));
 
                     return expr_bytes;
                 }
@@ -413,10 +503,14 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    size_in_bytes_t lhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.lhs);
 
                     fprintf(codegen->out, "    pop %%rcx\n");
-                    fprintf(codegen->out, "    shl %%cl, %s\n", get_reg_for(REG_ACCUMULATOR, lhs_bytes));
+                    fprintf(codegen->out,
+                            "    shl %%cl, %s\n",
+                            get_reg_for(REG_ACCUMULATOR, lhs_bytes));
 
                     return lhs_bytes;
                 }
@@ -426,22 +520,31 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    size_in_bytes_t lhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.lhs);
 
                     fprintf(codegen->out, "    pop %%rcx\n");
-                    fprintf(codegen->out, "    shr %%cl, %s\n", get_reg_for(REG_ACCUMULATOR, lhs_bytes));
+                    fprintf(codegen->out,
+                            "    shr %%cl, %s\n",
+                            get_reg_for(REG_ACCUMULATOR, lhs_bytes));
 
                     return lhs_bytes;
                 }
                 case AST_BINOP_BITWISE_XOR: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    size_in_bytes_t rhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    size_in_bytes_t lhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.lhs);
 
-                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+                    size_in_bytes_t expr_bytes =
+                        bytes_max(rhs_bytes, lhs_bytes);
 
                     fprintf(codegen->out, "    pop %%rcx\n");
                     fprintf(codegen->out,
@@ -453,13 +556,18 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
                 }
                 case AST_BINOP_BITWISE_AND: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    size_in_bytes_t rhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    size_in_bytes_t lhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.lhs);
 
-                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+                    size_in_bytes_t expr_bytes =
+                        bytes_max(rhs_bytes, lhs_bytes);
 
                     fprintf(codegen->out, "    pop %%rcx\n");
                     fprintf(codegen->out,
@@ -471,13 +579,18 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
                 }
                 case AST_BINOP_BITWISE_OR: {
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    size_in_bytes_t rhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    size_in_bytes_t lhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.lhs);
 
-                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+                    size_in_bytes_t expr_bytes =
+                        bytes_max(rhs_bytes, lhs_bytes);
 
                     fprintf(codegen->out, "    pop %%rcx\n");
                     fprintf(codegen->out,
@@ -488,16 +601,25 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
                     return expr_bytes;
                 }
                 case AST_BINOP_LOGICAL_AND: {
-                    size_t label_exit = codegen_linux_x86_64_get_next_label(codegen);
+                    size_t label_exit =
+                        codegen_linux_x86_64_get_next_label(codegen);
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
-                    fprintf(codegen->out, "    cmp $0, %s\n", get_reg_for(REG_ACCUMULATOR, lhs_bytes));
+                    size_in_bytes_t lhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.lhs);
+                    fprintf(codegen->out,
+                            "    cmp $0, %s\n",
+                            get_reg_for(REG_ACCUMULATOR, lhs_bytes));
                     fprintf(codegen->out, "    je .L%ld\n", label_exit);
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
-                    fprintf(codegen->out, "    cmp $0, %s\n", get_reg_for(REG_ACCUMULATOR, rhs_bytes));
+                    size_in_bytes_t rhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.rhs);
+                    fprintf(codegen->out,
+                            "    cmp $0, %s\n",
+                            get_reg_for(REG_ACCUMULATOR, rhs_bytes));
                     fprintf(codegen->out, "    je .L%ld\n", label_exit);
                     fprintf(codegen->out, "    mov $1, %%rax\n");
                     fprintf(codegen->out, ".L%ld:\n", label_exit);
@@ -505,17 +627,27 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
                     return 1;
                 }
                 case AST_BINOP_LOGICAL_OR: {
-                    size_t label_t = codegen_linux_x86_64_get_next_label(codegen);
-                    size_t label_f = codegen_linux_x86_64_get_next_label(codegen);
+                    size_t label_t =
+                        codegen_linux_x86_64_get_next_label(codegen);
+                    size_t label_f =
+                        codegen_linux_x86_64_get_next_label(codegen);
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
-                    fprintf(codegen->out, "    cmp $0, %s\n", get_reg_for(REG_ACCUMULATOR, lhs_bytes));
+                    size_in_bytes_t lhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.lhs);
+                    fprintf(codegen->out,
+                            "    cmp $0, %s\n",
+                            get_reg_for(REG_ACCUMULATOR, lhs_bytes));
                     fprintf(codegen->out, "    jne .L%ld\n", label_t);
 
                     fprintf(codegen->out, "    xor %%rax, %%rax\n");
-                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
-                    fprintf(codegen->out, "    cmp $0, %s\n", get_reg_for(REG_ACCUMULATOR, rhs_bytes));
+                    size_in_bytes_t rhs_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             bin_op.rhs);
+                    fprintf(codegen->out,
+                            "    cmp $0, %s\n",
+                            get_reg_for(REG_ACCUMULATOR, rhs_bytes));
                     fprintf(codegen->out, "    je .L%ld\n", label_f);
 
                     fprintf(codegen->out, ".L%ld:\n", label_t);
@@ -532,12 +664,16 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
                     symbol_t *symbol = scope_lookup(scope, ref.id);
                     assert(symbol);
 
-                    size_t offset = codegen_linux_x86_64_get_stack_offset(codegen, symbol);
+                    size_t offset =
+                        codegen_linux_x86_64_get_stack_offset(codegen, symbol);
 
                     codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
 
                     size_t type_size = type_to_bytes(symbol->type);
-                    fprintf(codegen->out, "    mov %s, -%ld(%%rbp)\n", get_reg_for(REG_ACCUMULATOR, type_size), offset);
+                    fprintf(codegen->out,
+                            "    mov %s, -%ld(%%rbp)\n",
+                            get_reg_for(REG_ACCUMULATOR, type_size),
+                            offset);
 
                     // FIXME: we don't support a = b = c
                     return 0;
@@ -553,9 +689,13 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
             ast_unary_op_t unary_op = expr_node->as_unary_op;
             switch (unary_op.kind) {
                 case AST_UNARY_BITWISE_NOT: {
-                    size_in_bytes_t expr_bytes = codegen_linux_x86_64_emit_expression(codegen, unary_op.expr);
+                    size_in_bytes_t expr_bytes =
+                        codegen_linux_x86_64_emit_expression(codegen,
+                                                             unary_op.expr);
 
-                    fprintf(codegen->out, "    not %s\n", get_reg_for(REG_ACCUMULATOR, expr_bytes));
+                    fprintf(codegen->out,
+                            "    not %s\n",
+                            get_reg_for(REG_ACCUMULATOR, expr_bytes));
 
                     return expr_bytes;
                 }
@@ -600,10 +740,12 @@ codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
                 symbol_t *symbol = scope_lookup(scope, var_def.id);
                 assert(symbol);
 
-                codegen_linux_x86_64_put_stack_offset(codegen, symbol, codegen->base_offset);
+                codegen_linux_x86_64_put_stack_offset(
+                    codegen, symbol, codegen->base_offset);
 
                 if (var_def.value) {
-                    codegen_linux_x86_64_emit_expression(codegen, var_def.value);
+                    codegen_linux_x86_64_emit_expression(codegen,
+                                                         var_def.value);
                 }
 
                 size_t type_size = type_to_bytes(symbol->type);
@@ -633,7 +775,8 @@ codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
                 ast_node_t *cond = while_stmt.cond;
                 ast_node_t *then = while_stmt.then;
 
-                size_t begin_label = codegen_linux_x86_64_get_next_label(codegen);
+                size_t begin_label =
+                    codegen_linux_x86_64_get_next_label(codegen);
                 size_t end_label = codegen_linux_x86_64_get_next_label(codegen);
 
                 fprintf(codegen->out, ".L%ld:\n", begin_label);
@@ -641,7 +784,8 @@ codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
                 fprintf(codegen->out, "    cmp $1, %%rax\n");
                 fprintf(codegen->out, "    jnz .L%ld\n", end_label);
 
-                assert(then->kind == AST_NODE_BLOCK && "invalid while-then block");
+                assert(then->kind == AST_NODE_BLOCK &&
+                       "invalid while-then block");
                 ast_block_t then_block = then->as_block;
 
                 codegen_linux_x86_64_emit_block(codegen, &then_block);
@@ -652,7 +796,8 @@ codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
                 break;
             }
             default: {
-                // FIXME: improve error: replace the node->kind to a string representation
+                // 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;
@@ -709,7 +854,8 @@ type_to_bytes(type_t *type)
             return 8;
         }
         case TYPE_UNKNOWN: {
-            assert(0 && "cannot calculate size of an unknown type: probably a parser issue.");
+            assert(0 && "cannot calculate size of an unknown type: probably a "
+                        "parser issue.");
         }
     }
 
@@ -739,7 +885,8 @@ calculate_fn_local_size(scope_t *scope)
     list_item_t *item = list_head(scope->children);
 
     while (item != NULL) {
-        size_t child_local_size = calculate_fn_local_size((scope_t *)item->value);
+        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;
@@ -752,7 +899,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_def)
+codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen,
+                                   ast_fn_definition_t *fn_def)
 {
     codegen->base_offset = X86_CALL_EIP_STACK_OFFSET;
 
@@ -763,7 +911,8 @@ codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen, ast_fn_definition_
     fprintf(codegen->out, "    mov %%rsp, %%rbp\n");
 
     size_t i = 0;
-    for (list_item_t *item = list_head(fn_def->params); item != NULL; item = list_next(item)) {
+    for (list_item_t *item = list_head(fn_def->params); item != NULL;
+         item = list_next(item)) {
         assert(i < X86_CALL_ARG_SIZE);
 
         ast_fn_param_t *param = item->value;
@@ -773,7 +922,8 @@ codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen, ast_fn_definition_
 
         size_t offset = codegen->base_offset;
 
-        codegen_linux_x86_64_put_stack_offset(codegen, symbol, codegen->base_offset);
+        codegen_linux_x86_64_put_stack_offset(
+            codegen, symbol, codegen->base_offset);
 
         fprintf(codegen->out,
                 "    mov %s, -%ld(%%rbp)\n",
@@ -799,7 +949,9 @@ codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen, ast_fn_definition_
 }
 
 static void
-codegen_linux_x86_64_put_stack_offset(codegen_x86_64_t *codegen, symbol_t *symbol, size_t offset)
+codegen_linux_x86_64_put_stack_offset(codegen_x86_64_t *codegen,
+                                      symbol_t *symbol,
+                                      size_t offset)
 {
 
     size_t *stack_offset = arena_alloc(codegen->arena, sizeof(size_t));
@@ -812,7 +964,8 @@ codegen_linux_x86_64_put_stack_offset(codegen_x86_64_t *codegen, symbol_t *symbo
 }
 
 static size_t
-codegen_linux_x86_64_get_stack_offset(codegen_x86_64_t *codegen, symbol_t *symbol)
+codegen_linux_x86_64_get_stack_offset(codegen_x86_64_t *codegen,
+                                      symbol_t *symbol)
 {
     char symbol_ptr[PTR_HEX_CSTR_SIZE];
     sprintf(symbol_ptr, "%lx", (uintptr_t)symbol);
diff --git a/src/codegen_linux_x86_64.h b/src/codegen_linux_x86_64.h
index a4137de..7e2841d 100644
--- a/src/codegen_linux_x86_64.h
+++ b/src/codegen_linux_x86_64.h
@@ -35,6 +35,7 @@ void
 codegen_linux_x86_64_init(codegen_x86_64_t *codegen, arena_t *arena, FILE *out);
 
 void
-codegen_linux_x86_64_emit_translation_unit(codegen_x86_64_t *codegen, ast_node_t *prog);
+codegen_linux_x86_64_emit_translation_unit(codegen_x86_64_t *codegen,
+                                           ast_node_t *prog);
 
 #endif /* CODEGEN_X86_64_H */
diff --git a/src/lexer.c b/src/lexer.c
index 03763de..1a6c24b 100644
--- a/src/lexer.c
+++ b/src/lexer.c
@@ -50,7 +50,10 @@ static void
 lexer_init_char_value_token(lexer_t *lexer, token_t *token, token_kind_t kind);
 
 static void
-lexer_init_str_value_token(lexer_t *lexer, token_t *token, token_kind_t kind, lexer_cursor_t cur);
+lexer_init_str_value_token(lexer_t *lexer,
+                           token_t *token,
+                           token_kind_t kind,
+                           lexer_cursor_t cur);
 
 static void
 lexer_init_eof_token(lexer_t *lexer, token_t *token);
@@ -95,7 +98,8 @@ lexer_next_token(lexer_t *lexer, token_t *token)
                 .size = lexer->cur.offset - start_cur.offset,
             };
 
-            lexer_init_str_value_token(lexer, token, lexer_str_to_token_kind(text), start_cur);
+            lexer_init_str_value_token(
+                lexer, token, lexer_str_to_token_kind(text), start_cur);
             return;
         }
 
@@ -117,7 +121,8 @@ lexer_next_token(lexer_t *lexer, token_t *token)
 
                 if (lexer_current_char(lexer) == '=') {
                     lexer_skip_char(lexer);
-                    lexer_init_str_value_token(lexer, token, TOKEN_CMP_EQ, start_cur);
+                    lexer_init_str_value_token(
+                        lexer, token, TOKEN_CMP_EQ, start_cur);
                     return;
                 }
 
@@ -130,7 +135,8 @@ lexer_next_token(lexer_t *lexer, token_t *token)
 
                 if (lexer_current_char(lexer) == '=') {
                     lexer_skip_char(lexer);
-                    lexer_init_str_value_token(lexer, token, TOKEN_CMP_NEQ, start_cur);
+                    lexer_init_str_value_token(
+                        lexer, token, TOKEN_CMP_NEQ, start_cur);
                     return;
                 }
 
@@ -143,7 +149,8 @@ lexer_next_token(lexer_t *lexer, token_t *token)
 
                 if (lexer_current_char(lexer) == '&') {
                     lexer_skip_char(lexer);
-                    lexer_init_str_value_token(lexer, token, TOKEN_LOGICAL_AND, start_cur);
+                    lexer_init_str_value_token(
+                        lexer, token, TOKEN_LOGICAL_AND, start_cur);
                     return;
                 }
 
@@ -156,7 +163,8 @@ lexer_next_token(lexer_t *lexer, token_t *token)
 
                 if (lexer_current_char(lexer) == '|') {
                     lexer_skip_char(lexer);
-                    lexer_init_str_value_token(lexer, token, TOKEN_LOGICAL_OR, start_cur);
+                    lexer_init_str_value_token(
+                        lexer, token, TOKEN_LOGICAL_OR, start_cur);
                     return;
                 }
 
@@ -170,16 +178,19 @@ lexer_next_token(lexer_t *lexer, token_t *token)
                 switch (lexer_current_char(lexer)) {
                     case '<': {
                         lexer_skip_char(lexer);
-                        lexer_init_str_value_token(lexer, token, TOKEN_BITWISE_LSHIFT, start_cur);
+                        lexer_init_str_value_token(
+                            lexer, token, TOKEN_BITWISE_LSHIFT, start_cur);
                         return;
                     }
                     case '=': {
                         lexer_skip_char(lexer);
-                        lexer_init_str_value_token(lexer, token, TOKEN_CMP_LEQ, start_cur);
+                        lexer_init_str_value_token(
+                            lexer, token, TOKEN_CMP_LEQ, start_cur);
                         return;
                     }
                     default: {
-                        lexer_init_str_value_token(lexer, token, TOKEN_LT, start_cur);
+                        lexer_init_str_value_token(
+                            lexer, token, TOKEN_LT, start_cur);
                         return;
                     }
                 }
@@ -191,16 +202,19 @@ lexer_next_token(lexer_t *lexer, token_t *token)
                 switch (lexer_current_char(lexer)) {
                     case '>': {
                         lexer_skip_char(lexer);
-                        lexer_init_str_value_token(lexer, token, TOKEN_BITWISE_RSHIFT, start_cur);
+                        lexer_init_str_value_token(
+                            lexer, token, TOKEN_BITWISE_RSHIFT, start_cur);
                         return;
                     }
                     case '=': {
                         lexer_skip_char(lexer);
-                        lexer_init_str_value_token(lexer, token, TOKEN_CMP_GEQ, start_cur);
+                        lexer_init_str_value_token(
+                            lexer, token, TOKEN_CMP_GEQ, start_cur);
                         return;
                     }
                     default: {
-                        lexer_init_str_value_token(lexer, token, TOKEN_GT, start_cur);
+                        lexer_init_str_value_token(
+                            lexer, token, TOKEN_GT, start_cur);
                         return;
                     }
                 }
@@ -421,7 +435,10 @@ lexer_init_char_value_token(lexer_t *lexer, token_t *token, token_kind_t kind)
 }
 
 static void
-lexer_init_str_value_token(lexer_t *lexer, token_t *token, token_kind_t kind, lexer_cursor_t cur)
+lexer_init_str_value_token(lexer_t *lexer,
+                           token_t *token,
+                           token_kind_t kind,
+                           lexer_cursor_t cur)
 {
     string_view_t str = {
         .chars = lexer->src.code.chars + cur.offset,
@@ -510,7 +527,8 @@ token_loc_to_line(token_loc_t loc)
         .size = 0,
     };
 
-    while ((line.size + offset) < loc.src.code.size && line.chars[line.size] != '\n' && line.chars[line.size] != 0) {
+    while ((line.size + offset) < loc.src.code.size &&
+           line.chars[line.size] != '\n' && line.chars[line.size] != 0) {
         ++line.size;
     }
 
diff --git a/src/main.c b/src/main.c
index aa09f34..2b02564 100644
--- a/src/main.c
+++ b/src/main.c
@@ -158,7 +158,8 @@ handle_codegen_linux(cli_opts_t *opts)
         } else if (strcmp(opts->arch, "aarch64") == 0) {
             codegen_linux_aarch64_emit_translation_unit(out, ast);
         } else {
-            fprintf(stderr, "error: architecture '%s' not supported\n", opts->arch);
+            fprintf(
+                stderr, "error: architecture '%s' not supported\n", opts->arch);
             cli_print_usage(stderr, opts->compiler_path);
             exit(EXIT_FAILURE);
         }
@@ -171,7 +172,11 @@ 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));
+    sprintf(command,
+            "%s/bin/as %s -o " SV_FMT ".o",
+            opts->sysroot,
+            asm_file,
+            SV_ARG(opts->output_bin));
 
     int exit_code = system(command);
 
@@ -210,7 +215,10 @@ read_entire_file(char *filepath, arena_t *arena)
     FILE *stream = fopen(filepath, "rb");
 
     if (stream == NULL) {
-        fprintf(stderr, "error: could not open file %s: %s\n", filepath, strerror(errno));
+        fprintf(stderr,
+                "error: could not open file %s: %s\n",
+                filepath,
+                strerror(errno));
         exit(EXIT_FAILURE);
     }
 
@@ -225,7 +233,10 @@ read_entire_file(char *filepath, arena_t *arena)
     code.chars = (char *)arena_alloc(arena, (size_t)code.size);
 
     if (code.chars == NULL) {
-        fprintf(stderr, "error: could not read file %s: %s\n", filepath, strerror(errno));
+        fprintf(stderr,
+                "error: could not read file %s: %s\n",
+                filepath,
+                strerror(errno));
         exit(EXIT_FAILURE);
     }
 
diff --git a/src/map.c b/src/map.c
index 42801b0..97db39e 100644
--- a/src/map.c
+++ b/src/map.c
@@ -42,7 +42,8 @@ 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));
+        fprintf(
+            stderr, "[FATAL] Out of memory: map_new: %s\n", strerror(errno));
         exit(EXIT_FAILURE);
     }
     map->arena = arena;
@@ -54,11 +55,13 @@ 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));
+    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));
+        fprintf(
+            stderr, "[FATAL] Out of memory: map_init: %s\n", strerror(errno));
         exit(EXIT_FAILURE);
     }
     map->capacity = MAP_INITIAL_CAPACITY;
@@ -101,7 +104,8 @@ map_put(map_t *map, char *key, void *value)
             break;
         }
         if (entry->next == NULL) {
-            entry->next = (map_entry_t *)arena_alloc(map->arena, sizeof(map_entry_t));
+            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,
diff --git a/src/parser.c b/src/parser.c
index 177195d..4282f0a 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -150,7 +150,10 @@ token_kind_to_binary_op_kind(token_kind_t kind)
         case TOKEN_EQ:
             return AST_BINOP_ASSIGN;
         default: {
-            fprintf(stderr, "error: token kind (%s) not compatible with binary op kind\n", token_kind_to_cstr(kind));
+            fprintf(
+                stderr,
+                "error: token kind (%s) not compatible with binary op kind\n",
+                token_kind_to_cstr(kind));
             assert(false);
         }
     }
@@ -228,7 +231,8 @@ token_kind_to_unary_op_kind(token_kind_t token_kind)
         case TOKEN_BANG:
             return AST_UNARY_LOGICAL_NOT;
         default:
-            assert(false && "unable to covert the token_kind_t to unary_op_kind_t");
+            assert(false &&
+                   "unable to covert the token_kind_t to unary_op_kind_t");
     }
 }
 
@@ -251,12 +255,18 @@ parser_parse_expr_1(parser_t *parser, ast_node_t *lhs, size_t prev_precedence)
         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));
+               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);
         }
 
-        lhs = ast_new_node_bin_op(parser->arena, token_op.loc, token_kind_to_binary_op_kind(token_op.kind), lhs, rhs);
+        lhs = ast_new_node_bin_op(parser->arena,
+                                  token_op.loc,
+                                  token_kind_to_binary_op_kind(token_op.kind),
+                                  lhs,
+                                  rhs);
         if (lhs == NULL) {
             return NULL;
         }
@@ -284,7 +294,8 @@ parser_parse_factor(parser_t *parser)
 
     switch (token.kind) {
         case TOKEN_NUMBER:
-            return ast_new_node_literal_u32(parser->arena, token.loc, string_view_to_u32(token.value));
+            return ast_new_node_literal_u32(
+                parser->arena, token.loc, string_view_to_u32(token.value));
 
         case TOKEN_ID: {
             token_t token_id = token;
@@ -293,9 +304,11 @@ parser_parse_factor(parser_t *parser)
 
             if (token.kind == TOKEN_OPAREN) {
                 list_t *args = parser_parse_fn_args(parser);
-                return ast_new_node_fn_call(parser->arena, token_id.loc, token_id.value, args);
+                return ast_new_node_fn_call(
+                    parser->arena, token_id.loc, token_id.value, args);
             }
-            return ast_new_node_ref(parser->arena, token_id.loc, token_id.value);
+            return ast_new_node_ref(
+                parser->arena, token_id.loc, token_id.value);
         }
         case TOKEN_AND:
         case TOKEN_STAR:
@@ -325,7 +338,9 @@ parser_parse_factor(parser_t *parser)
             return expr;
         }
         default: {
-            fprintf(stderr, "error: parse_factor: unsupported or invalid token (%s)\n", token_kind_to_cstr(token.kind));
+            fprintf(stderr,
+                    "error: parse_factor: unsupported or invalid token (%s)\n",
+                    token_kind_to_cstr(token.kind));
             assert(false);
         }
     }
@@ -340,7 +355,9 @@ parser_parse_fn_args(parser_t *parser)
 
     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));
+        fprintf(stderr,
+                "[FATAL] Out of memory: parser_parse_fn_args: %s\n",
+                strerror(errno));
         exit(EXIT_FAILURE);
     }
 
@@ -382,7 +399,9 @@ parser_parse_fn_params(parser_t *parser)
 
     list_t *params = arena_alloc(parser->arena, sizeof(list_t));
     if (params == NULL) {
-        fprintf(stderr, "[FATAL] Out of memory: parser_parse_fn_params: %s\n", strerror(errno));
+        fprintf(stderr,
+                "[FATAL] Out of memory: parser_parse_fn_params: %s\n",
+                strerror(errno));
         exit(EXIT_FAILURE);
     }
 
@@ -410,7 +429,8 @@ parser_parse_fn_params(parser_t *parser)
             return NULL;
         }
 
-        ast_fn_param_t *param = ast_new_fn_param(parser->arena, token.value, type);
+        ast_fn_param_t *param =
+            ast_new_fn_param(parser->arena, token.value, type);
         list_append(params, param);
 
         skip_line_feeds(parser->lexer);
@@ -460,7 +480,12 @@ parser_parse_fn_definition(parser_t *parser)
         return NULL;
     }
 
-    return ast_new_node_fn_def(parser->arena, fn_name_token.loc, fn_name_token.value, params, ret_type, block);
+    return ast_new_node_fn_def(parser->arena,
+                               fn_name_token.loc,
+                               fn_name_token.value,
+                               params,
+                               ret_type,
+                               block);
 }
 
 static type_t *
@@ -492,7 +517,8 @@ parser_parse_type(parser_t *parser)
         }
         string_view_t ptr_id = token.value;
 
-        ptr_id.size = ptr_token.value.chars - token.value.chars + ptr_token.value.size;
+        ptr_id.size =
+            ptr_token.value.chars - token.value.chars + ptr_token.value.size;
 
         return type_new_ptr(parser->arena, ptr_id, type);
     }
@@ -581,7 +607,8 @@ parser_parse_return_stmt(parser_t *parser)
         return NULL;
     }
 
-    ast_node_t *node_return_stmt = ast_new_node_return_stmt(parser->arena, token_ret.loc, expr);
+    ast_node_t *node_return_stmt =
+        ast_new_node_return_stmt(parser->arena, token_ret.loc, expr);
     assert(node_return_stmt);
 
     return node_return_stmt;
@@ -632,7 +659,8 @@ parser_parse_if_stmt(parser_t *parser)
         }
     }
 
-    ast_node_t *node_if_stmt = ast_new_node_if_stmt(parser->arena, token_if.loc, cond, then, _else);
+    ast_node_t *node_if_stmt =
+        ast_new_node_if_stmt(parser->arena, token_if.loc, cond, then, _else);
 
     assert(node_if_stmt);
 
@@ -664,7 +692,8 @@ parser_parse_while_stmt(parser_t *parser)
     token_t next_token;
     peek_next_non_lf_token(parser->lexer, &next_token);
 
-    ast_node_t *node_while_stmt = ast_new_node_while_stmt(parser->arena, token_while.loc, cond, then);
+    ast_node_t *node_while_stmt =
+        ast_new_node_while_stmt(parser->arena, token_while.loc, cond, then);
 
     assert(node_while_stmt);
 
@@ -698,7 +727,8 @@ parser_parse_var_def(parser_t *parser)
         return NULL;
     }
 
-    ast_node_t *var_node = ast_new_node_var_def(parser->arena, token_id.loc, token_id.value, type, expr);
+    ast_node_t *var_node = ast_new_node_var_def(
+        parser->arena, token_id.loc, token_id.value, type, expr);
 
     return var_node;
 }
@@ -711,7 +741,9 @@ skip_expected_token(parser_t *parser, token_kind_t expected_kind)
 }
 
 static bool
-expected_next_token(parser_t *parser, token_t *token, token_kind_t expected_kind)
+expected_next_token(parser_t *parser,
+                    token_t *token,
+                    token_kind_t expected_kind)
 {
     lexer_next_token(parser->lexer, token);
     return expected_token(token, expected_kind);
@@ -722,7 +754,8 @@ expected_token(token_t *token, token_kind_t expected_kind)
 {
     if (token->kind != expected_kind) {
         fprintf(stderr,
-                "%s:%lu:%lu: syntax error: got '" SV_FMT "' token but expect '%s'\n",
+                "%s:%lu:%lu: syntax error: got '" SV_FMT
+                "' token but expect '%s'\n",
                 token->loc.src.filepath,
                 token_loc_to_lineno(token->loc),
                 token_loc_to_colno(token->loc),
diff --git a/src/pretty_print_ast.c b/src/pretty_print_ast.c
index 387cde4..d2164eb 100644
--- a/src/pretty_print_ast.c
+++ b/src/pretty_print_ast.c
@@ -80,7 +80,10 @@ pretty_print_print_ident(uint64_t *prefix, size_t level, bool lst_children)
 }
 
 static void
-pretty_print_tree(pretty_print_node_t *node, uint64_t *prefix, size_t level, bool lst_children)
+pretty_print_tree(pretty_print_node_t *node,
+                  uint64_t *prefix,
+                  size_t level,
+                  bool lst_children)
 {
     pretty_print_print_ident(prefix, level, lst_children);
 
@@ -94,7 +97,8 @@ pretty_print_tree(pretty_print_node_t *node, uint64_t *prefix, size_t level, boo
 
     size_t size = list_size(list);
     for (size_t i = 0; i < size; ++i) {
-        pretty_print_node_t *it = (pretty_print_node_t *)list_get(list, i)->value;
+        pretty_print_node_t *it =
+            (pretty_print_node_t *)list_get(list, i)->value;
         pretty_print_tree(it, prefix, level + 1, i + 1 == size);
     }
 }
@@ -102,7 +106,8 @@ pretty_print_tree(pretty_print_node_t *node, uint64_t *prefix, size_t level, boo
 static pretty_print_node_t *
 pretty_print_node_new(arena_t *arena)
 {
-    pretty_print_node_t *node = (pretty_print_node_t *)arena_alloc(arena, sizeof(pretty_print_node_t));
+    pretty_print_node_t *node =
+        (pretty_print_node_t *)arena_alloc(arena, sizeof(pretty_print_node_t));
     node->children = (list_t *)arena_alloc(arena, sizeof(list_t));
     list_init(node->children, arena);
     return node;
@@ -113,7 +118,10 @@ pretty_print_new_fn_param(ast_fn_param_t *param, arena_t *arena)
 {
     pretty_print_node_t *node = pretty_print_node_new(arena);
     char name[256];
-    sprintf(name, "Param_Definition <name:" SV_FMT "> <type:" SV_FMT ">", SV_ARG(param->id), SV_ARG(param->type->id));
+    sprintf(name,
+            "Param_Definition <name:" SV_FMT "> <type:" SV_FMT ">",
+            SV_ARG(param->id),
+            SV_ARG(param->type->id));
     node->name = (char *)arena_alloc(arena, sizeof(char) * (strlen(name) + 1));
     strcpy(node->name, name);
     return node;
@@ -132,7 +140,8 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
             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);
+                pretty_print_node_t *fn_node =
+                    ast_node_to_pretty_print_node(decl, arena);
                 list_append(node->children, fn_node);
 
                 item = list_next(item);
@@ -149,16 +158,19 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
                     "Function_Definition <name:" SV_FMT "> <return:" SV_FMT ">",
                     SV_ARG(fn_def.id),
                     SV_ARG(fn_def.return_type->id));
-            node->name = (char *)arena_alloc(arena, sizeof(char) * (strlen(name) + 1));
+            node->name =
+                (char *)arena_alloc(arena, sizeof(char) * (strlen(name) + 1));
             strcpy(node->name, name);
 
             list_item_t *param = list_head(fn_def.params);
             while (param != NULL) {
-                list_append(node->children, pretty_print_new_fn_param(param->value, arena));
+                list_append(node->children,
+                            pretty_print_new_fn_param(param->value, arena));
                 param = list_next(param);
             }
 
-            pretty_print_node_t *block = ast_node_to_pretty_print_node(fn_def.block, arena);
+            pretty_print_node_t *block =
+                ast_node_to_pretty_print_node(fn_def.block, arena);
             list_append(node->children, block);
             return node;
         }
@@ -167,13 +179,16 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *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));
+            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));
+                list_append(node->children,
+                            ast_node_to_pretty_print_node(item->value, arena));
                 item = list_next(item);
             }
 
@@ -187,8 +202,10 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
 
             size_t block_nodes_size = list_size(block.nodes);
             for (size_t i = 0; i < block_nodes_size; ++i) {
-                ast_node_t *ast_node = (ast_node_t *)list_get(block.nodes, i)->value;
-                pretty_print_node_t *child = ast_node_to_pretty_print_node(ast_node, arena);
+                ast_node_t *ast_node =
+                    (ast_node_t *)list_get(block.nodes, i)->value;
+                pretty_print_node_t *child =
+                    ast_node_to_pretty_print_node(ast_node, arena);
                 list_append(node->children, child);
             }
             return node;
@@ -199,7 +216,8 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
 
             node->name = "Return_Statement";
 
-            pretty_print_node_t *child = ast_node_to_pretty_print_node(return_stmt.expr, arena);
+            pretty_print_node_t *child =
+                ast_node_to_pretty_print_node(return_stmt.expr, arena);
             list_append(node->children, child);
 
             return node;
@@ -210,7 +228,8 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
 
             node->name = "If_Statement";
 
-            pretty_print_node_t *child = ast_node_to_pretty_print_node(if_stmt.cond, arena);
+            pretty_print_node_t *child =
+                ast_node_to_pretty_print_node(if_stmt.cond, arena);
             list_append(node->children, child);
 
             child = ast_node_to_pretty_print_node(if_stmt.then, arena);
@@ -229,7 +248,8 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
 
             node->name = "While_Statement";
 
-            pretty_print_node_t *child = ast_node_to_pretty_print_node(while_stmt.cond, arena);
+            pretty_print_node_t *child =
+                ast_node_to_pretty_print_node(while_stmt.cond, arena);
             list_append(node->children, child);
 
             child = ast_node_to_pretty_print_node(while_stmt.then, arena);
@@ -244,8 +264,10 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
             char name[256];
             switch (literal.kind) {
                 case AST_LITERAL_U32: {
-                    sprintf(name, "Literal <kind:u32> <value:%u>", literal.as_u32);
-                    node->name = (char *)arena_alloc(arena, sizeof(char) * (strlen(name) + 1));
+                    sprintf(
+                        name, "Literal <kind:u32> <value:%u>", literal.as_u32);
+                    node->name = (char *)arena_alloc(
+                        arena, sizeof(char) * (strlen(name) + 1));
                     strcpy(node->name, name);
                     break;
                 }
@@ -260,11 +282,16 @@ 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:" SV_FMT ">", SV_ARG(var.id), SV_ARG(var.type->id));
-            node->name = (char *)arena_alloc(arena, sizeof(char) * (strlen(name) + 1));
+            sprintf(name,
+                    "Var_Definition <name:" SV_FMT "> <kind:" SV_FMT ">",
+                    SV_ARG(var.id),
+                    SV_ARG(var.type->id));
+            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);
+            pretty_print_node_t *child =
+                ast_node_to_pretty_print_node(var.value, arena);
             list_append(node->children, child);
 
             return node;
@@ -275,7 +302,8 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
 
             char name[256];
             sprintf(name, "Reference <name:" SV_FMT ">", SV_ARG(ref.id));
-            node->name = (char *)arena_alloc(arena, sizeof(char) * (strlen(name) + 1));
+            node->name =
+                (char *)arena_alloc(arena, sizeof(char) * (strlen(name) + 1));
             strcpy(node->name, name);
 
             return node;
@@ -365,8 +393,10 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
                     assert(false && "binop not implemented");
             }
 
-            pretty_print_node_t *lhs = ast_node_to_pretty_print_node(binop.lhs, arena);
-            pretty_print_node_t *rhs = ast_node_to_pretty_print_node(binop.rhs, arena);
+            pretty_print_node_t *lhs =
+                ast_node_to_pretty_print_node(binop.lhs, arena);
+            pretty_print_node_t *rhs =
+                ast_node_to_pretty_print_node(binop.rhs, arena);
 
             list_append(node->children, lhs);
             list_append(node->children, rhs);
@@ -405,7 +435,8 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
                 }
             }
 
-            pretty_print_node_t *expr = ast_node_to_pretty_print_node(unary_op.expr, arena);
+            pretty_print_node_t *expr =
+                ast_node_to_pretty_print_node(unary_op.expr, arena);
             list_append(node->children, expr);
 
             return node;
diff --git a/src/scope.c b/src/scope.c
index e483fbe..274982f 100644
--- a/src/scope.c
+++ b/src/scope.c
@@ -28,7 +28,8 @@ scope_new(arena_t *arena)
     assert(arena);
     scope_t *scope = (scope_t *)arena_alloc(arena, sizeof(scope_t));
     if (scope == NULL) {
-        fprintf(stderr, "[FATAL] Out of memory: scope_new: %s\n", strerror(errno));
+        fprintf(
+            stderr, "[FATAL] Out of memory: scope_new: %s\n", strerror(errno));
         exit(EXIT_FAILURE);
     }
     scope->arena = arena;
@@ -38,7 +39,8 @@ scope_new(arena_t *arena)
     list_t *children = (list_t *)arena_alloc(arena, sizeof(list_t));
 
     if (children == NULL) {
-        fprintf(stderr, "[FATAL] Out of memory: scope_new: %s\n", strerror(errno));
+        fprintf(
+            stderr, "[FATAL] Out of memory: scope_new: %s\n", strerror(errno));
         exit(EXIT_FAILURE);
     }
 
@@ -54,7 +56,8 @@ symbol_new(arena_t *arena, string_view_t id, type_t *type)
     assert(arena);
     symbol_t *symbol = (symbol_t *)arena_alloc(arena, sizeof(symbol_t));
     if (symbol == NULL) {
-        fprintf(stderr, "[FATAL] Out of memory: symbol_new: %s\n", strerror(errno));
+        fprintf(
+            stderr, "[FATAL] Out of memory: symbol_new: %s\n", strerror(errno));
         exit(EXIT_FAILURE);
     }
     symbol->id = id;
diff --git a/tests/unit/arena_test.c b/tests/unit/arena_test.c
index a471572..1c16492 100644
--- a/tests/unit/arena_test.c
+++ b/tests/unit/arena_test.c
@@ -84,11 +84,27 @@ arena_padding_test(const MunitParameter params[], void *user_data_or_fixture)
     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 };
+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 };
 
 int
 main(int argc, char *argv[])
diff --git a/tests/unit/list_test.c b/tests/unit/list_test.c
index 8b759f9..72e7788 100644
--- a/tests/unit/list_test.c
+++ b/tests/unit/list_test.c
@@ -95,12 +95,33 @@ list_next_test(const MunitParameter params[], void *user_data_or_fixture)
     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 = { "/list", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE };
+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 = { "/list",
+                                  tests,
+                                  NULL,
+                                  1,
+                                  MUNIT_SUITE_OPTION_NONE };
 
 int
 main(int argc, char *argv[])
diff --git a/tests/unit/map_test.c b/tests/unit/map_test.c
index 9497c7c..2be431e 100644
--- a/tests/unit/map_test.c
+++ b/tests/unit/map_test.c
@@ -92,12 +92,26 @@ test_map_get_kvs(const MunitParameter params[], void *user_data_or_fixture)
 }
 
 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 },
+    { "/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 };
+static const MunitSuite suite = { "/map",
+                                  tests,
+                                  NULL,
+                                  1,
+                                  MUNIT_SUITE_OPTION_NONE };
 
 int
 main(int argc, char *argv[])
diff --git a/tests/unit/string_view_test.c b/tests/unit/string_view_test.c
index d85f19b..9600c77 100644
--- a/tests/unit/string_view_test.c
+++ b/tests/unit/string_view_test.c
@@ -22,7 +22,8 @@
 #include <string.h>
 
 static MunitResult
-string_view_eq_to_cstr_test(const MunitParameter params[], void *user_data_or_fixture)
+string_view_eq_to_cstr_test(const MunitParameter params[],
+                            void *user_data_or_fixture)
 {
     char *name = "John Doe";
 
@@ -46,7 +47,8 @@ string_view_eq_to_cstr_test(const MunitParameter params[], void *user_data_or_fi
 }
 
 static MunitResult
-string_view_to_u32_test(const MunitParameter params[], void *user_data_or_fixture)
+string_view_to_u32_test(const MunitParameter params[],
+                        void *user_data_or_fixture)
 {
     char *number = "69";
 
@@ -68,12 +70,26 @@ string_view_to_u32_test(const MunitParameter params[], void *user_data_or_fixtur
 }
 
 static MunitTest tests[] = {
-    { "/eq_to_cstr_test", string_view_eq_to_cstr_test, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
-    { "/to_u32_test", string_view_to_u32_test, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
+    { "/eq_to_cstr_test",
+      string_view_eq_to_cstr_test,
+      NULL,
+      NULL,
+      MUNIT_TEST_OPTION_NONE,
+      NULL },
+    { "/to_u32_test",
+      string_view_to_u32_test,
+      NULL,
+      NULL,
+      MUNIT_TEST_OPTION_NONE,
+      NULL },
     { NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }
 };
 
-static const MunitSuite suite = { "/string_view", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE };
+static const MunitSuite suite = { "/string_view",
+                                  tests,
+                                  NULL,
+                                  1,
+                                  MUNIT_SUITE_OPTION_NONE };
 
 int
 main(int argc, char *argv[])
-- 
2.46.1


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1] tests: show test name into test execution output
@ 2024-10-11 12:26 Carlos Maniero
  2024-10-11 12:26 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-11 12:26 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

The test output was printing a bunch of dots *.* during the test
execution. Now the tests are printing in the following format:

./0002_binary_operator_addition.ol                           [ OK ] [  222ms ]
./0005_binary_operator_reminder.ol                           [ OK ] [  264ms ]
./0004_binary_operator_division.ol                           [ OK ] [  259ms ]
./0003_binary_operator_multiplication.ol                     [ OK ] [  252ms ]
./0006_binary_operator_subtraction.ol                        [ OK ] [  260ms ]
./0008_binary_operator_lt.ol                                 [ OK ] [  231ms ]

The script changed from multiple printf calls into a single call to make
sure the scripts can be executed in parallel without breaking the
output.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 tests/olc/run.sh | 59 +++++++++++++++++++++++++-----------------------
 1 file changed, 31 insertions(+), 28 deletions(-)

diff --git a/tests/olc/run.sh b/tests/olc/run.sh
index 503f960..033ab5b 100755
--- a/tests/olc/run.sh
+++ b/tests/olc/run.sh
@@ -28,7 +28,6 @@ TEST_INVOCATION=""
 TEST_LINE_NUMBER=""
 TEST_CONTENTS_PATH=""
 TEST_ARGS=""
-TEST_SOME_PASSED=""
 
 # UI
 COLOR_RED=1
@@ -37,17 +36,21 @@ COLOR_YELLOW=3
 COLOR_CYAN=6
 COLOR_GRAY=7
 
+if [ -t 1 ]; then
+  if tput setaf 1 > /dev/null 2>&1; then
+    HAS_COLORS=1
+  fi
+fi
+
 colored() {
   text="$1"
 
-  if [ -t 1 ]; then
-    if tput setaf 1 > /dev/null 2>&1; then
-      color=$(tput setaf "$2")
-      reset=$(tput sgr0)
+  if [ -n "$HAS_COLORS" ]; then
+    color=$(tput setaf "$2")
+    reset=$(tput sgr0)
 
-      printf "%s%s%s" "$color" "$text" "$reset"
-      return
-    fi
+    printf "%s%s%s" "$color" "$text" "$reset"
+    return
   fi
 
   printf "%s" "$text"
@@ -58,22 +61,12 @@ colored() {
 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"
+  printf "%s:%s:1: %s %s: %s\n" \
+    "$TEST_FILE" \
+    "$TEST_LINE_NUMBER" \
+    "$(colored "failed" "$COLOR_RED")" \
+    "$(colored "$TEST_INVOCATION" "$COLOR_CYAN")" \
+    "$reason"
 }
 # end test output
 
@@ -88,9 +81,9 @@ expect_output_contains() {
 
     if [ "$(grep "$(printf "%s" "$expected_line" | sed 's/\\/\\\\/g')" "$actual_file" | wc -c)" = "0" ]; then
       print_failed
-      colored "(not found) $expected_line" $COLOR_YELLOW
+      colored "(not found) $expected_line" "$COLOR_YELLOW"
       echo
-      colored "$(awk '{print "(actual) " $0}' "$actual_file")" $COLOR_GRAY
+      colored "$(awk '{print "(actual) " $0}' "$actual_file")" "$COLOR_GRAY"
       echo
       exit 1
     fi
@@ -119,6 +112,8 @@ cleanup() {
 main() {
   all_test_end="$(grep -n "# END" "$TEST_FILE" | awk -F: '{print $1}')"
 
+  ts=$(date +%s%N)
+
   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$//')"
@@ -147,14 +142,22 @@ main() {
 
     if type "$TEST_NAME" | grep -q 'function'; then
         "$TEST_NAME"
-        print_passed
     else
         print_failed "test not implemented"
     fi
   done || exit 1;
 
+  elapsed=$((($(date +%s%N) - ts)/1000000))
+
+  padsize=$((59 - ${#TEST_FILE}))
+
+  printf "%s %*s [ %s ] [ % 4dms ]\n" \
+    "$TEST_FILE" \
+    $(( padsize > 0 ? padsize : 0 )) "" \
+    "$(colored "OK" "$COLOR_GREEN")"\
+    $elapsed
+
   cleanup
-  echo
 }
 
 get_test_args() {

base-commit: 3a988ec943cd6b97d9fd6ff255dcdc387f80516c
-- 
2.46.1


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1] build: turn make build output less noisy
@ 2024-10-11  4:02 Johnny Richard
  2024-10-11  2:04 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Johnny Richard @ 2024-10-11  4:02 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 Makefile            | 16 +++++++++-------
 tests/unit/Makefile |  6 ++++--
 2 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/Makefile b/Makefile
index 3a799ae..58526ac 100644
--- a/Makefile
+++ b/Makefile
@@ -74,20 +74,21 @@ olang.info: docs/info/*.texi
 	$(MAKEINFO) docs/info/olang.texi
 
 $(TARGET): $(BUILDDIR) $(OBJS)
-	$(CC) $(CFLAGS) $(OBJS) -o $(TARGET)
+	@$(CC) $(CFLAGS) $(OBJS) -o $(TARGET)
+	@printf 'CCLD\t%s\n' '$@'
 
 $(BUILDDIR):
 	@mkdir -p $@
 
 .PHONY: format
 format: $(SRCS) $(HEADERS)
-	clang-format --dry-run --Werror $?
-	$(MAKE) -C tests/unit/ format
+	@clang-format --dry-run --Werror $?
+	@$(MAKE) --no-print-directory -C tests/unit/ format
 
 .PHONY: format-fix
 format-fix: $(SRCS) $(HEADERS)
-	clang-format -i $?
-	$(MAKE) -C tests/unit/ format-fix
+	@clang-format -i $?
+	@$(MAKE) --no-print-directory -C tests/unit/ format-fix
 
 .PHONY: check-olc
 check-olc: $(TARGET)
@@ -100,7 +101,7 @@ check-unit: $(TARGET)
 .PHONY: clean
 clean:
 	@rm -f olang.info
-	$(MAKE) -C tests/unit/ clean
+	@$(MAKE) --no-print-directory -C tests/unit/ clean
 	@rm -rf build/ $(TARGET)
 
 .PHONY: check
@@ -115,4 +116,5 @@ docs-dist:
 	$(MAKE) -C docs dist
 
 $(BUILDDIR)/%.o: $(SRCDIR)/%.c
-	$(CC) $(CFLAGS) -c $< -o $@
+	@$(CC) $(CFLAGS) -c $< -o $@
+	@printf 'CC\t%s\n' '$@'
diff --git a/tests/unit/Makefile b/tests/unit/Makefile
index 686938f..0a5c5aa 100644
--- a/tests/unit/Makefile
+++ b/tests/unit/Makefile
@@ -11,6 +11,7 @@ all: $(RUN_TESTS)
 
 %.bin: %.c $(MUNIT)
 	@$(CC) $(CFLAGS) $(MUNIT) $(DEP_OBJS) $< -o $@
+	@printf 'CCLD\t%s\n' '$@'
 
 %.run: %.bin
 	@./$<
@@ -20,10 +21,11 @@ clean:
 	@$(RM) -rfv lib
 
 format: $(SRCS)
-	clang-format --dry-run --Werror $?
+	@clang-format --dry-run --Werror $?
 
 format-fix: $(SRCS)
-	clang-format -i $?
+	@clang-format -i $?
 
 $(MUNIT):
 	@$(CC) -c $(MUNIT_SRC) $(CFLAGS) -o $(MUNIT)
+	@printf 'CCLD\t%s\n' '$@'

base-commit: 6b93fd0059d5d5e9923cad79ad2f0f2938f793ed
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1] build: turn make build output less noisy
@ 2024-10-11  4:02 Johnny Richard
  2024-10-11  2:03 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Johnny Richard @ 2024-10-11  4:02 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 Makefile            | 16 +++++++++-------
 tests/unit/Makefile |  6 ++++--
 2 files changed, 13 insertions(+), 9 deletions(-)

diff --git a/Makefile b/Makefile
index 3a799ae..58526ac 100644
--- a/Makefile
+++ b/Makefile
@@ -74,20 +74,21 @@ olang.info: docs/info/*.texi
 	$(MAKEINFO) docs/info/olang.texi
 
 $(TARGET): $(BUILDDIR) $(OBJS)
-	$(CC) $(CFLAGS) $(OBJS) -o $(TARGET)
+	@$(CC) $(CFLAGS) $(OBJS) -o $(TARGET)
+	@printf 'CCLD\t%s\n' '$@'
 
 $(BUILDDIR):
 	@mkdir -p $@
 
 .PHONY: format
 format: $(SRCS) $(HEADERS)
-	clang-format --dry-run --Werror $?
-	$(MAKE) -C tests/unit/ format
+	@clang-format --dry-run --Werror $?
+	@$(MAKE) --no-print-directory -C tests/unit/ format
 
 .PHONY: format-fix
 format-fix: $(SRCS) $(HEADERS)
-	clang-format -i $?
-	$(MAKE) -C tests/unit/ format-fix
+	@clang-format -i $?
+	@$(MAKE) --no-print-directory -C tests/unit/ format-fix
 
 .PHONY: check-olc
 check-olc: $(TARGET)
@@ -100,7 +101,7 @@ check-unit: $(TARGET)
 .PHONY: clean
 clean:
 	@rm -f olang.info
-	$(MAKE) -C tests/unit/ clean
+	@$(MAKE) --no-print-directory -C tests/unit/ clean
 	@rm -rf build/ $(TARGET)
 
 .PHONY: check
@@ -115,4 +116,5 @@ docs-dist:
 	$(MAKE) -C docs dist
 
 $(BUILDDIR)/%.o: $(SRCDIR)/%.c
-	$(CC) $(CFLAGS) -c $< -o $@
+	@$(CC) $(CFLAGS) -c $< -o $@
+	@printf 'CC\t%s\n' '$@'
diff --git a/tests/unit/Makefile b/tests/unit/Makefile
index 686938f..0a5c5aa 100644
--- a/tests/unit/Makefile
+++ b/tests/unit/Makefile
@@ -11,6 +11,7 @@ all: $(RUN_TESTS)
 
 %.bin: %.c $(MUNIT)
 	@$(CC) $(CFLAGS) $(MUNIT) $(DEP_OBJS) $< -o $@
+	@printf 'CCLD\t%s\n' '$@'
 
 %.run: %.bin
 	@./$<
@@ -20,10 +21,11 @@ clean:
 	@$(RM) -rfv lib
 
 format: $(SRCS)
-	clang-format --dry-run --Werror $?
+	@clang-format --dry-run --Werror $?
 
 format-fix: $(SRCS)
-	clang-format -i $?
+	@clang-format -i $?
 
 $(MUNIT):
 	@$(CC) -c $(MUNIT_SRC) $(CFLAGS) -o $(MUNIT)
+	@printf 'CCLD\t%s\n' '$@'

base-commit: 6b93fd0059d5d5e9923cad79ad2f0f2938f793ed
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1] spec: add contrib bin script for validating olang spec
@ 2024-10-11  3:24 Johnny Richard
  2024-10-11  1:25 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Johnny Richard @ 2024-10-11  3:24 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

The new contrib script was executed against all tests files and few
adjustments on the spec was made:

  1) Add comments support
  2) New unsigned types added
  3) Types support pointers (*)
  4) Optional spaces over functions params and args

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 .build.yml                   |  3 ++
 contrib/bin/ebnf.clj         | 42 +++++++++++++++++
 contrib/bin/run-ebnf         | 22 +++++++++
 docs/info/olang.ebnf         | 89 +++++++++++++++++++++++++++++++++++
 docs/info/specification.texi | 91 +-----------------------------------
 5 files changed, 157 insertions(+), 90 deletions(-)
 create mode 100644 contrib/bin/ebnf.clj
 create mode 100755 contrib/bin/run-ebnf
 create mode 100644 docs/info/olang.ebnf

diff --git a/.build.yml b/.build.yml
index 8265da2..9c8eaa5 100644
--- a/.build.yml
+++ b/.build.yml
@@ -21,6 +21,9 @@ tasks:
   - check: |
       cd olang
       make check
+  - check-spec: |
+      cd olang
+      ./contrib/bin/run-ebnf <(find tests/olc/ -name '*.ol' | xargs -L1 grep -Ev '(^#|^$)' | cat) 1> /dev/null
   - docs-publish: |
       cd olang
       if [ "$BUILD_REASON" = "" ]
diff --git a/contrib/bin/ebnf.clj b/contrib/bin/ebnf.clj
new file mode 100644
index 0000000..b972c1f
--- /dev/null
+++ b/contrib/bin/ebnf.clj
@@ -0,0 +1,42 @@
+; 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/>.
+
+(require '[clojure.pprint :refer [pprint]])
+(require '[instaparse.core :refer [parser]])
+(require '[instaparse.failure :refer [pprint-failure]])
+
+(def olang-file (System/getenv "FILE"))
+
+(def olang-parser (parser (slurp *in*)))
+
+(def parser-result (olang-parser (slurp olang-file)))
+
+(defn println-err
+  ([ ] (.println *err* ""))
+  ([s] (.println *err* s)))
+
+(defn print-err
+  ([ ] (.print *err* ""))
+  ([s] (.print *err* s)))
+
+(defn main []
+  (if (:reason parser-result)
+    (with-redefs [clojure.core/println println-err
+                  clojure.core/print print-err]
+      (pprint-failure parser-result)
+      (System/exit 1))
+    (pprint parser-result)))
+
+(main)
diff --git a/contrib/bin/run-ebnf b/contrib/bin/run-ebnf
new file mode 100755
index 0000000..4b2b559
--- /dev/null
+++ b/contrib/bin/run-ebnf
@@ -0,0 +1,22 @@
+#!/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/>.
+
+BASEDIR="$(dirname "$0")"
+
+DEPS='{:deps {instaparse/instaparse {:mvn/version "1.5.0"}}}'
+
+cat $BASEDIR/../../docs/info/olang.ebnf | sed 's/<\([a-z-]*\)>/\1/g' | FILE="$1" clj -Sdeps "$DEPS" -M $BASEDIR/ebnf.clj
diff --git a/docs/info/olang.ebnf b/docs/info/olang.ebnf
new file mode 100644
index 0000000..76a5c2f
--- /dev/null
+++ b/docs/info/olang.ebnf
@@ -0,0 +1,89 @@
+(* Entry Point *)
+<translation-unit> ::= (<ows> (<external-declaration> | <comment>) <ows> (<end-of-statement> | <end-of-file>))*
+
+(* Translation Unit *)
+<external-declaration> ::= <common-statement> | <function-definition>
+
+(* Variables *)
+<variable-definition>  ::= 'var' <ws> <variable-name> <ows> ':' <ows> <type> <ows> <variable-initializer>?
+<constant-definition>  ::= 'const' <ws> <variable-name> <ows> ':' <ows> <type> <ows> <variable-initializer>
+<variable-name>        ::= <identifier>
+<variable-initializer> ::= '=' <ows> <expression>
+
+(* Functions *)
+<function-definition> ::= 'fn' <ws> <function-name> <ows> '(' ( <ows> | <ows> <function-params> <ows> ) ')' <ows> ':' <ows> <return-type> <ows> <function-body>
+<function-name>       ::= <identifier>
+<function-params>     ::= <identifier> <ows> ':' <ows> <type> ( <ows> ',' <ows> <function-params>)*
+<return-type>         ::= <type>
+<function-body>       ::= <block>
+<block>               ::= '{' <ows> <statement> <ows> (<end-of-statement> <ows> <statement> <ows>)* <end-of-statement>? <ows> '}'
+<function-args>       ::= <expression> (<ows> ',' <ows> <function-args>)*
+<function-call>       ::= <function-name> <ows> '(' ( <ows> | <ows> <function-args> <ows> ) ')'
+<statement>           ::= <common-statement> | <if-statement> | <while-statement> | <return-statement> | <function-call>
+<if-statement>        ::= 'if' <ws> <expression> <ows> <block> ( <ows> 'else' ( <ows> <block> | <ows> <if-statement> ) )?
+<while-statement>     ::= 'while' <ws> <expression> <ows> <block>
+<return-statement>    ::= 'return' <ws> <expression>
+
+(* Statements *)
+<end-of-statement>    ::= ';' | <line-break>
+<common-statement>    ::= <variable-definition> | <constant-definition> | <expression>
+<assignment-operator> ::= '='
+                        | '*='
+                        | '/='
+                        | '%='
+                        | '+='
+                        | '-='
+                        | '<<='
+                        | '>>='
+                        | '&='
+                        | '^='
+                        | '|='
+
+(* Expressions *)
+<expression>                ::= <binary-expression>
+<binary-expression>         ::= <assignment-expression>
+<assignment-expression>     ::= <logical-or-expression> (<ows> <assignment-operator> <ows> <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-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>)*
+<primary-expression>        ::= <integer-literal>
+                              | <variable-name>
+                              | <function-call>
+                              | <unary-expression>
+                              | '(' <ows> <expression> <ows> ')'
+<unary-expression>          ::= <unary-operator> <ows> <primary-expression>
+<unary-operator>            ::= '&'
+                              | '*'
+                              | '+'
+                              | '-'
+                              | '~'
+                              | '!'
+
+(* Identifiers *)
+<type>       ::= ('u8' | 'u16' | 'u32' | 'u64') (<ows> '*')*
+
+<identifier> ::= (<alpha> | '_') (<alpha> | <digit> | '_')*
+
+(* Literals *)
+<integer-literal> ::= <integer-base10> | <integer-base16>
+<integer-base10>  ::= #'[1-9]' (<digit> | '_')* | '0'
+<integer-base16>  ::= #'0[Xx]' <hex-digit> (<hex-digit> | '_')*
+
+(* Utilities *)
+<comment>      ::= '#' #'[^\n]*'
+<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/info/specification.texi b/docs/info/specification.texi
index e1c9d76..707d932 100644
--- a/docs/info/specification.texi
+++ b/docs/info/specification.texi
@@ -19,93 +19,4 @@ This is the O Programming Language EBNF grammar specification[^1]
 NOTE: This grammar spec is a DRAFT and it covers only a small portion of the
 language.
 
-@verbatim
-(* Entry Point *)
-<translation-unit>     ::= (<ows> <external-declaration> <ows> (<end-of-statement> | <end-of-file>))*
-
-(* Translation Unit *)
-<external-declaration> ::= <common-statement> | <function-definition>
-
-(* Variables *)
-<variable-definition>   ::= 'var' <ws> <variable-name> <ows> ':' <ows> <type> <ows> <variable-initializer>?
-<constant-definition>   ::= 'const' <ws> <variable-name> <ows> ':' <ows> <type> <ows> <variable-initializer>
-<variable-name>         ::= <identifier>
-<variable-initializer>  ::= '=' <ows> <expression>
-
-(* Functions *)
-<function-definition> ::= 'fn' <ws> <function-name> <ows> '(' ( <ows> | <ows> <function-params> <ows> ) ')' <ows> ':' <ows> <return-type> <ows> <function-body>
-<function-name>       ::= <identifier>
-<function-params>     ::= <identifier> <ows> ':' <ows> <type> ( <ows> ',' <function-params>)*
-<return-type>         ::= <type>
-<function-body>       ::= <block>
-<block>               ::= '{' <ows> <statement> <ows> (<end-of-statement> <ows> <statement> <ows>)* <end-of-statement>? <ows> '}'
-<function-args>       ::= <expression> (<ows> ',' <function-args>)*
-<function-call>       ::= <function-name> <ows> '(' ( <ows> | <ows> <function-args> <ows> ) ')'
-<statement>           ::= <common-statement> | <if-statement> | <while-statement> | <return-statement> | <function-call>
-<if-statement>        ::= 'if' <ws> <expression> <ows> <block> ( <ows> 'else' ( <ows> <block> | <ows> <if-statement> ) )?
-<while-statement>     ::= 'while' <ws> <expression> <ows> <block>
-<return-statement>    ::= 'return' <ws> <expression>
-
-(* Statements *)
-<end-of-statement>    ::= ';' | <line-break>
-<common-statement>    ::= <variable-definition> | <constant-definition> | <expression>
-<assignment-operator> ::= '='
-                        | '*='
-                        | '/='
-                        | '%='
-                        | '+='
-                        | '-='
-                        | '<<='
-                        | '>>='
-                        | '&='
-                        | '^='
-                        | '|='
-
-(* Expressions *)
-<expression> ::= <binary-expression>
-<binary-expression> ::= <assignment-expression>
-<assignment-expression> ::= <logical-or-expression> (<ows> <assignment-operator> <ows> <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-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>)*
-<primary-expression> ::= <integer-literal>
-                       | <variable-name>
-                       | <function-call>
-                       | <unary-expression>
-                       | '(' <ows>  <expression> <ows> ')'
-
-<unary-expression> ::= <unary-operator> <ows> <primary-expression>
-<unary-operator> ::= '&'
-                   | '*'
-                   | '+'
-                   | '-'
-                   | '~'
-                   | '!'
-
-(* Identifiers *)
-<type>                ::= 'u32'
-<identifier>          ::= (<alpha> | '_') (<alpha> | <digit> | '_')*
-
-(* Literals *)
-<integer-literal>     ::= <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>         ::= #'$'
-@end verbatim
+@verbatiminclude olang.ebnf

base-commit: 3c15dde88c22d4d4703b23cbb1b29490371e9128
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v3 2/2] parser: fix unary precedence
@ 2024-10-10 16:29 Carlos Maniero
  2024-10-10 16:30 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-10 16:29 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

Unary is only supposed to contain primary expressions. Before this
commit, the code { *a = 1 } was interpreted as { *(a = 1) }.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 src/parser.c               |  2 +-
 tests/olc/0034_pointers.ol | 43 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 44 insertions(+), 1 deletion(-)
 create mode 100644 tests/olc/0034_pointers.ol

diff --git a/src/parser.c b/src/parser.c
index 44a4900..177195d 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -303,7 +303,7 @@ parser_parse_factor(parser_t *parser)
         case TOKEN_DASH:
         case TOKEN_TILDE:
         case TOKEN_BANG: {
-            ast_node_t *expr = parser_parse_expr(parser);
+            ast_node_t *expr = parser_parse_factor(parser);
             if (expr == NULL) {
                 return NULL;
             }
diff --git a/tests/olc/0034_pointers.ol b/tests/olc/0034_pointers.ol
new file mode 100644
index 0000000..3f3e80b
--- /dev/null
+++ b/tests/olc/0034_pointers.ol
@@ -0,0 +1,43 @@
+# 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 {
+  var a: u32 = 1
+  var b: u32* = &a
+  *b = 0
+  return a
+}
+
+# xTEST test_compile(exit_code=0)
+#
+# xTEST test_run_binary(exit_code=0)
+#
+# TEST test_ast WITH
+# Translation_Unit
+# `-Function_Definition <name:main> <return:u32>
+#   `-Block
+#     |-Var_Definition <name:a> <kind:u32>
+#     | `-Literal <kind:u32> <value:1>
+#     |-Var_Definition <name:b> <kind:u32*>
+#     | `-Unary_Operation (&)
+#     |   `-Reference <name:a>
+#     |-Binary_Operation (=)
+#     | |-Unary_Operation (*)
+#     | | `-Reference <name:b>
+#     | `-Literal <kind:u32> <value:0>
+#     `-Return_Statement
+#       `-Reference <name:a>
+# END
-- 
2.46.1


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v2] spec: parser: enable expression on function level
@ 2024-10-10 12:07 Carlos Maniero
  2024-10-10 12:08 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-10 12:07 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

Previously, assignment statements were restricted to variable
assignments. This commit extends their functionality by:

    1. Enabling unary operators on the left-hand side (LHS) of
       assignments: This allows for expressions like { *a = 1 } to be
       valid.

    2. Permitting function calls within assignment statements: This
       enables calling functions without explicitly capturing their
       return values, as demonstrated in { my_fn() }.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
v2:
  add missing expression on <common-statement>

 docs/info/specification.texi | 18 +++++++--------
 src/parser.c                 | 44 ++++++++----------------------------
 2 files changed, 19 insertions(+), 43 deletions(-)

diff --git a/docs/info/specification.texi b/docs/info/specification.texi
index a466331..8653f79 100644
--- a/docs/info/specification.texi
+++ b/docs/info/specification.texi
@@ -48,8 +48,7 @@ language.
 
 (* Statements *)
 <end-of-statement>    ::= ';' | <line-break>
-<common-statement>    ::= <variable-definition> | <constant-definition> | <assignment>
-<assignment>          ::= <variable-name> <ows> <assignment-operator> <ows> <expression>
+<common-statement>    ::= <variable-definition> | <constant-definition> | <expression>
 <assignment-operator> ::= '='
                         | '*='
                         | '/='
@@ -74,7 +73,8 @@ language.
 <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> ::= <assignment-expression> (<ows> ('*' | '/' | '%') <ows> <assignment-expression>)*
+<assignment-expression> ::= <primary-expression> (<ows> <assignment-operator> <ows> <primary-expression>)*
 <primary-expression> ::= <integer-literal>
                        | <variable-name>
                        | <function-call>
@@ -82,12 +82,12 @@ language.
                        | '(' <ows>  <expression> <ows> ')'
 
 <unary-expression> ::= <unary-operator> <ows> <primary-expression>
-<unary-operator> ::= &
-                   | *
-                   | +
-                   | -
-                   | ~
-                   | !
+<unary-operator> ::= '&'
+                   | '*'
+                   | '+'
+                   | '-'
+                   | '~'
+                   | '!'
 
 (* Identifiers *)
 <type>                ::= 'u32'
diff --git a/src/parser.c b/src/parser.c
index f712bfc..12a7e2c 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -52,9 +52,6 @@ parser_parse_while_stmt(parser_t *parser);
 static ast_node_t *
 parser_parse_var_def(parser_t *parser);
 
-static ast_node_t *
-parser_parse_var_assign_stmt(parser_t *parser);
-
 static ast_node_t *
 parser_parse_fn_definition(parser_t *parser);
 
@@ -292,6 +289,15 @@ parser_parse_factor(parser_t *parser)
             if (token.kind == TOKEN_OPAREN) {
                 list_t *args = parser_parse_fn_args(parser);
                 return ast_new_node_fn_call(parser->arena, token_id.loc, token_id.value, args);
+            } else if (token.kind == TOKEN_EQ) {
+                if (!skip_expected_token(parser, TOKEN_EQ)) {
+                    return NULL;
+                }
+
+                ast_node_t *ref = ast_new_node_ref(parser->arena, token_id.loc, token_id.value);
+                ast_node_t *expr = parser_parse_expr(parser);
+
+                return ast_new_node_var_assign_stmt(parser->arena, token.loc, ref, expr);
             }
 
             return ast_new_node_ref(parser->arena, token_id.loc, token_id.value);
@@ -536,20 +542,11 @@ StartLoop:
             node = parser_parse_var_def(parser);
             break;
         }
-        case TOKEN_ID: {
-            lexer_lookahead(parser->lexer, &next_token, 2);
-            if (!expected_token(&next_token, TOKEN_EQ)) {
-                return NULL;
-            }
-            node = parser_parse_var_assign_stmt(parser);
-            break;
-        }
         case TOKEN_CCURLY: {
             goto EndLoop;
         }
         default: {
-            // FIXME: write a better error message
-            goto EndLoop;
+            node = parser_parse_expr(parser);
         }
     }
 
@@ -711,27 +708,6 @@ parser_parse_var_def(parser_t *parser)
     return var_node;
 }
 
-static ast_node_t *
-parser_parse_var_assign_stmt(parser_t *parser)
-{
-    token_t token_id;
-
-    if (!expected_next_token(parser, &token_id, TOKEN_ID)) {
-        return NULL;
-    }
-
-    token_t token_eq;
-
-    if (!expected_next_token(parser, &token_eq, TOKEN_EQ)) {
-        return NULL;
-    }
-
-    ast_node_t *ref = ast_new_node_ref(parser->arena, token_id.loc, token_id.value);
-    ast_node_t *expr = parser_parse_expr(parser);
-
-    return ast_new_node_var_assign_stmt(parser->arena, token_eq.loc, ref, expr);
-}
-
 static bool
 skip_expected_token(parser_t *parser, token_kind_t expected_kind)
 {

base-commit: d45df92d81f00059ecd1f6478bac7e4511d9fd8d
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang] spec: parser: enable expression on function level
@ 2024-10-10 11:46 Carlos Maniero
  2024-10-10 11:47 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-10 11:46 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

Previously, assignment statements were restricted to variable
assignments. This commit extends their functionality by:

    1. Enabling unary operators on the left-hand side (LHS) of
       assignments: This allows for expressions like { *a = 1 } to be
       valid.

    2. Permitting function calls within assignment statements: This
       enables calling functions without explicitly capturing their
       return values, as demonstrated in { my_fn() }.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 docs/info/specification.texi | 18 +++++++--------
 src/parser.c                 | 44 ++++++++----------------------------
 2 files changed, 19 insertions(+), 43 deletions(-)

diff --git a/docs/info/specification.texi b/docs/info/specification.texi
index a466331..921644a 100644
--- a/docs/info/specification.texi
+++ b/docs/info/specification.texi
@@ -48,8 +48,7 @@ language.
 
 (* Statements *)
 <end-of-statement>    ::= ';' | <line-break>
-<common-statement>    ::= <variable-definition> | <constant-definition> | <assignment>
-<assignment>          ::= <variable-name> <ows> <assignment-operator> <ows> <expression>
+<common-statement>    ::= <variable-definition> | <constant-definition>
 <assignment-operator> ::= '='
                         | '*='
                         | '/='
@@ -74,7 +73,8 @@ language.
 <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> ::= <assignment-expression> (<ows> ('*' | '/' | '%') <ows> <assignment-expression>)*
+<assignment-expression> ::= <primary-expression> (<ows> <assignment-operator> <ows> <primary-expression>)*
 <primary-expression> ::= <integer-literal>
                        | <variable-name>
                        | <function-call>
@@ -82,12 +82,12 @@ language.
                        | '(' <ows>  <expression> <ows> ')'
 
 <unary-expression> ::= <unary-operator> <ows> <primary-expression>
-<unary-operator> ::= &
-                   | *
-                   | +
-                   | -
-                   | ~
-                   | !
+<unary-operator> ::= '&'
+                   | '*'
+                   | '+'
+                   | '-'
+                   | '~'
+                   | '!'
 
 (* Identifiers *)
 <type>                ::= 'u32'
diff --git a/src/parser.c b/src/parser.c
index f712bfc..12a7e2c 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -52,9 +52,6 @@ parser_parse_while_stmt(parser_t *parser);
 static ast_node_t *
 parser_parse_var_def(parser_t *parser);
 
-static ast_node_t *
-parser_parse_var_assign_stmt(parser_t *parser);
-
 static ast_node_t *
 parser_parse_fn_definition(parser_t *parser);
 
@@ -292,6 +289,15 @@ parser_parse_factor(parser_t *parser)
             if (token.kind == TOKEN_OPAREN) {
                 list_t *args = parser_parse_fn_args(parser);
                 return ast_new_node_fn_call(parser->arena, token_id.loc, token_id.value, args);
+            } else if (token.kind == TOKEN_EQ) {
+                if (!skip_expected_token(parser, TOKEN_EQ)) {
+                    return NULL;
+                }
+
+                ast_node_t *ref = ast_new_node_ref(parser->arena, token_id.loc, token_id.value);
+                ast_node_t *expr = parser_parse_expr(parser);
+
+                return ast_new_node_var_assign_stmt(parser->arena, token.loc, ref, expr);
             }
 
             return ast_new_node_ref(parser->arena, token_id.loc, token_id.value);
@@ -536,20 +542,11 @@ StartLoop:
             node = parser_parse_var_def(parser);
             break;
         }
-        case TOKEN_ID: {
-            lexer_lookahead(parser->lexer, &next_token, 2);
-            if (!expected_token(&next_token, TOKEN_EQ)) {
-                return NULL;
-            }
-            node = parser_parse_var_assign_stmt(parser);
-            break;
-        }
         case TOKEN_CCURLY: {
             goto EndLoop;
         }
         default: {
-            // FIXME: write a better error message
-            goto EndLoop;
+            node = parser_parse_expr(parser);
         }
     }
 
@@ -711,27 +708,6 @@ parser_parse_var_def(parser_t *parser)
     return var_node;
 }
 
-static ast_node_t *
-parser_parse_var_assign_stmt(parser_t *parser)
-{
-    token_t token_id;
-
-    if (!expected_next_token(parser, &token_id, TOKEN_ID)) {
-        return NULL;
-    }
-
-    token_t token_eq;
-
-    if (!expected_next_token(parser, &token_eq, TOKEN_EQ)) {
-        return NULL;
-    }
-
-    ast_node_t *ref = ast_new_node_ref(parser->arena, token_id.loc, token_id.value);
-    ast_node_t *expr = parser_parse_expr(parser);
-
-    return ast_new_node_var_assign_stmt(parser->arena, token_eq.loc, ref, expr);
-}
-
 static bool
 skip_expected_token(parser_t *parser, token_kind_t expected_kind)
 {

base-commit: d45df92d81f00059ecd1f6478bac7e4511d9fd8d
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1 5/6] codestyle: add trailing comma on struct initializer
@ 2024-10-10  1:33 Carlos Maniero
  2024-10-10  1:34 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-10  1:33 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

It makes the struct to be formatted that way:

  {
    .a = 1,
    .b = 2,
  }

Instead of:

  { .a = 1,
    .b = 2 }

Unfortunately, there is no config on clang-format to enforce this.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 src/cli.c                     |  5 +++-
 src/lexer.c                   | 51 +++++++++++++++++++++++++++++------
 src/main.c                    |  5 +++-
 src/map.c                     | 14 ++++++++--
 src/string_view.c             |  5 +++-
 tests/unit/string_view_test.c | 20 +++++++++++---
 6 files changed, 83 insertions(+), 17 deletions(-)

diff --git a/src/cli.c b/src/cli.c
index d57945a..1f30ea3 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -34,7 +34,10 @@ cli_opts_parse_sysroot(cli_opts_t *opts, cli_args_t *args);
 
 cli_opts_t
 cli_parse_args(int argc, char **argv) {
-    cli_args_t args = { .argc = argc, .argv = argv };
+    cli_args_t args = {
+        .argc = argc,
+        .argv = argv,
+    };
     cli_opts_t opts = { 0 };
 
     opts.compiler_path = cli_args_shift(&args);
diff --git a/src/lexer.c b/src/lexer.c
index 352f979..c11bacf 100644
--- a/src/lexer.c
+++ b/src/lexer.c
@@ -88,8 +88,10 @@ lexer_next_token(lexer_t *lexer, token_t *token) {
                 current_char = lexer_current_char(lexer);
             }
 
-            string_view_t text = { .chars = lexer->src.code.chars + start_cur.offset,
-                                   .size = lexer->cur.offset - start_cur.offset };
+            string_view_t text = {
+                .chars = lexer->src.code.chars + start_cur.offset,
+                .size = lexer->cur.offset - start_cur.offset,
+            };
 
             lexer_init_str_value_token(lexer, token, lexer_str_to_token_kind(text), start_cur);
             return;
@@ -392,20 +394,50 @@ _isspace(char c) {
 
 static void
 lexer_init_char_value_token(lexer_t *lexer, token_t *token, token_kind_t kind) {
-    string_view_t str = { .chars = lexer->src.code.chars + lexer->cur.offset, .size = 1 };
-    *token = (token_t){ .kind = kind, .value = str, .loc = (token_loc_t){ .src = lexer->src, .cur = lexer->cur } };
+    string_view_t str = {
+        .chars = lexer->src.code.chars + lexer->cur.offset,
+        .size = 1,
+    };
+    *token = (token_t){
+        .kind = kind,
+        .value = str,
+        .loc =
+            (token_loc_t){
+                .src = lexer->src,
+                .cur = lexer->cur,
+            },
+    };
 }
 
 static void
 lexer_init_str_value_token(lexer_t *lexer, token_t *token, token_kind_t kind, lexer_cursor_t cur) {
-    string_view_t str = { .chars = lexer->src.code.chars + cur.offset, .size = lexer->cur.offset - cur.offset };
-    *token = (token_t){ .kind = kind, .value = str, .loc = (token_loc_t){ .src = lexer->src, .cur = cur } };
+    string_view_t str = {
+        .chars = lexer->src.code.chars + cur.offset,
+        .size = lexer->cur.offset - cur.offset,
+    };
+    *token = (token_t){
+        .kind = kind,
+        .value = str,
+        .loc =
+            (token_loc_t){
+                .src = lexer->src,
+                .cur = cur,
+            },
+    };
 }
 
 static void
 lexer_init_eof_token(lexer_t *lexer, token_t *token) {
     string_view_t str = { 0 };
-    *token = (token_t){ .kind = TOKEN_EOF, .value = str, .loc = (token_loc_t){ .src = lexer->src, .cur = lexer->cur } };
+    *token = (token_t){
+        .kind = TOKEN_EOF,
+        .value = str,
+        .loc =
+            (token_loc_t){
+                .src = lexer->src,
+                .cur = lexer->cur,
+            },
+    };
 }
 
 static token_kind_t
@@ -456,7 +488,10 @@ lexer_lookahead(lexer_t *lexer, token_t *token, size_t n) {
 string_view_t
 token_loc_to_line(token_loc_t loc) {
     size_t offset = loc.cur.bol;
-    string_view_t line = { .chars = loc.src.code.chars + offset, .size = 0 };
+    string_view_t line = {
+        .chars = loc.src.code.chars + offset,
+        .size = 0,
+    };
 
     while ((line.size + offset) < loc.src.code.size && line.chars[line.size] != '\n' && line.chars[line.size] != 0) {
         ++line.size;
diff --git a/src/main.c b/src/main.c
index bb0e451..0e174a1 100644
--- a/src/main.c
+++ b/src/main.c
@@ -235,7 +235,10 @@ read_entire_file(char *filepath, arena_t *arena) {
 
     fclose(stream);
 
-    return (source_code_t){ .filepath = filepath, .code = code };
+    return (source_code_t){
+        .filepath = filepath,
+        .code = code,
+    };
 }
 
 static void
diff --git a/src/map.c b/src/map.c
index 9109eaa..31100b4 100644
--- a/src/map.c
+++ b/src/map.c
@@ -82,7 +82,12 @@ map_put(map_t *map, char *key, void *value) {
     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 };
+        *entry = (map_entry_t){
+            .key = _strdup(key, map->arena),
+            .hash = hash,
+            .value = value,
+            .next = NULL,
+        };
         return true;
     }
 
@@ -93,7 +98,12 @@ map_put(map_t *map, char *key, void *value) {
         }
         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 };
+            *entry->next = (map_entry_t){
+                .key = _strdup(key, map->arena),
+                .hash = hash,
+                .value = value,
+                .next = NULL,
+            };
 
             break;
         }
diff --git a/src/string_view.c b/src/string_view.c
index 22cb61e..438174c 100644
--- a/src/string_view.c
+++ b/src/string_view.c
@@ -23,7 +23,10 @@
 
 string_view_t
 string_view_from_cstr(char *cstr) {
-    return (string_view_t){ .chars = cstr, .size = strlen(cstr) };
+    return (string_view_t){
+        .chars = cstr,
+        .size = strlen(cstr),
+    };
 }
 
 bool
diff --git a/tests/unit/string_view_test.c b/tests/unit/string_view_test.c
index f8ca792..6f42e0c 100644
--- a/tests/unit/string_view_test.c
+++ b/tests/unit/string_view_test.c
@@ -25,14 +25,20 @@ static MunitResult
 string_view_eq_to_cstr_test(const MunitParameter params[], void *user_data_or_fixture) {
     char *name = "John Doe";
 
-    string_view_t str = { .chars = name, .size = strlen(name) };
+    string_view_t str = {
+        .chars = name,
+        .size = strlen(name),
+    };
 
     assert_true(string_view_eq_to_cstr(str, "John Doe"));
     assert_false(string_view_eq_to_cstr(str, "Doe"));
 
     char *return_stmt = "return EXIT_SUCCESS;";
 
-    str = (string_view_t){ .chars = return_stmt + 7, .size = 12 };
+    str = (string_view_t){
+        .chars = return_stmt + 7,
+        .size = 12,
+    };
     assert_true(string_view_eq_to_cstr(str, "EXIT_SUCCESS"));
 
     return MUNIT_OK;
@@ -42,11 +48,17 @@ static MunitResult
 string_view_to_u32_test(const MunitParameter params[], void *user_data_or_fixture) {
     char *number = "69";
 
-    string_view_t str = { .chars = number, .size = strlen(number) };
+    string_view_t str = {
+        .chars = number,
+        .size = strlen(number),
+    };
 
     assert_uint32(string_view_to_u32(str), ==, 69);
 
-    str = (string_view_t){ .chars = "39;", .size = 2 };
+    str = (string_view_t){
+        .chars = "39;",
+        .size = 2,
+    };
 
     assert_uint32(string_view_to_u32(str), ==, 39);
 
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1 3/3] parser: codegen(x86_64): add bitwise not unary op support
@ 2024-10-09 21:18 Johnny Richard
  2024-10-09 19:35 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Johnny Richard @ 2024-10-09 21:18 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Currently, all compiler tests rely on the exit code returned by a binary
program, which restricts our testing to 8-bit unsigned numbers.

To effectively test the bitwise-not unary operation within a main
function, we utilize UINT32_MAX (4294967295), which represents a value
with all bits set. The result of applying the bitwise-not operation to 0
is then used as the exit code.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/ast.c                                    | 14 ++++++++
 src/ast.h                                    | 22 ++++++++++++
 src/checker.c                                |  7 ++++
 src/codegen_linux_x86_64.c                   | 18 ++++++++++
 src/parser.c                                 | 35 ++++++++++++++++++++
 src/pretty_print_ast.c                       | 22 +++++++++++-
 tests/olc/0033_unary_operator_bitwise_not.ol | 16 +++++++--
 7 files changed, 131 insertions(+), 3 deletions(-)

diff --git a/src/ast.c b/src/ast.c
index 79daca6..5a36ccb 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -117,6 +117,20 @@ ast_new_node_bin_op(arena_t *arena, token_loc_t loc, ast_binary_op_kind_t kind,
     return node_bin_op;
 }
 
+ast_node_t *
+ast_new_node_unary_op(arena_t *arena, token_loc_t loc, ast_unary_op_kind_t kind, ast_node_t *expr)
+{
+    ast_node_t *node_unary_op = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
+    assert(node_unary_op);
+
+    node_unary_op->kind = AST_NODE_UNARY_OP;
+    node_unary_op->loc = loc;
+    node_unary_op->as_unary_op.kind = kind;
+    node_unary_op->as_unary_op.expr = expr;
+
+    return node_unary_op;
+}
+
 ast_node_t *
 ast_new_node_literal_u32(arena_t *arena, token_loc_t loc, uint32_t value)
 {
diff --git a/src/ast.h b/src/ast.h
index fccc303..ee94f57 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -36,6 +36,7 @@ typedef enum
     AST_NODE_FN_CALL,
     AST_NODE_VAR_DEF,
     AST_NODE_BINARY_OP,
+    AST_NODE_UNARY_OP,
     AST_NODE_VAR_ASSIGN_STMT,
     AST_NODE_RETURN_STMT,
     AST_NODE_IF_STMT,
@@ -148,6 +149,23 @@ typedef struct ast_binary_op
     ast_node_t *rhs;
 } ast_binary_op_t;
 
+typedef enum ast_unary_op_kind
+{
+    AST_UNARY_BITWISE_NOT,
+    AST_UNARY_LOGICAL_NOT,
+    AST_UNARY_NEGATIVE,
+    AST_UNARY_POSITIVE,
+    AST_UNARY_DEREFERENCE,
+    AST_UNARY_ADDRESSOF,
+} ast_unary_op_kind_t;
+
+typedef struct ast_unary_op
+{
+    ast_node_meta_t meta;
+    ast_unary_op_kind_t kind;
+    ast_node_t *expr;
+} ast_unary_op_t;
+
 typedef struct ast_var_assign_stmt
 {
     ast_node_meta_t meta;
@@ -189,6 +207,7 @@ typedef union ast_node
     ast_fn_call_t as_fn_call;
     ast_var_definition_t as_var_def;
     ast_binary_op_t as_bin_op;
+    ast_unary_op_t as_unary_op;
     ast_literal_t as_literal;
     ast_ref_t as_ref;
     ast_block_t as_block;
@@ -218,6 +237,9 @@ ast_new_node_var_def(arena_t *arena, token_loc_t loc, string_view_t id, type_t *
 ast_node_t *
 ast_new_node_bin_op(arena_t *arena, token_loc_t loc, ast_binary_op_kind_t kind, ast_node_t *lhs, ast_node_t *rhs);
 
+ast_node_t *
+ast_new_node_unary_op(arena_t *arena, token_loc_t loc, ast_unary_op_kind_t kind, ast_node_t *expr);
+
 ast_node_t *
 ast_new_node_literal_u32(arena_t *arena, token_loc_t loc, uint32_t value);
 
diff --git a/src/checker.c b/src/checker.c
index 7a9a7b6..62d612f 100644
--- a/src/checker.c
+++ b/src/checker.c
@@ -177,6 +177,13 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
             return;
         }
 
+        case AST_NODE_UNARY_OP: {
+            ast_unary_op_t unary_op = ast->as_unary_op;
+
+            populate_scope(checker, scope, unary_op.expr);
+            return;
+        }
+
         case AST_NODE_VAR_ASSIGN_STMT: {
             ast_var_assign_stmt_t var_assign_stmt = ast->as_var_assign_stmt;
 
diff --git a/src/codegen_linux_x86_64.c b/src/codegen_linux_x86_64.c
index ac268ad..ae28aa5 100644
--- a/src/codegen_linux_x86_64.c
+++ b/src/codegen_linux_x86_64.c
@@ -530,6 +530,24 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
                 }
             }
         }
+
+        case AST_NODE_UNARY_OP: {
+            ast_unary_op_t unary_op = expr_node->as_unary_op;
+            switch (unary_op.kind) {
+                case AST_UNARY_BITWISE_NOT: {
+                    size_in_bytes_t expr_bytes = codegen_linux_x86_64_emit_expression(codegen, unary_op.expr);
+
+                    fprintf(codegen->out, "    not %s\n", get_reg_for(REG_ACCUMULATOR, expr_bytes));
+
+                    return expr_bytes;
+                }
+                default: {
+                    assert(0 && "unsupported unary operation");
+                    return 0;
+                }
+            }
+        }
+
         default:
             assert(0 && "unsupported expression");
     }
diff --git a/src/parser.c b/src/parser.c
index 0bfa282..f712bfc 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -209,6 +209,27 @@ get_binary_op_precedence(token_kind_t kind)
     }
 }
 
+static ast_unary_op_kind_t
+token_kind_to_unary_op_kind(token_kind_t token_kind)
+{
+    switch (token_kind) {
+        case TOKEN_AND:
+            return AST_UNARY_ADDRESSOF;
+        case TOKEN_STAR:
+            return AST_UNARY_DEREFERENCE;
+        case TOKEN_PLUS:
+            return AST_UNARY_POSITIVE;
+        case TOKEN_DASH:
+            return AST_UNARY_NEGATIVE;
+        case TOKEN_TILDE:
+            return AST_UNARY_BITWISE_NOT;
+        case TOKEN_BANG:
+            return AST_UNARY_LOGICAL_NOT;
+        default:
+            assert(false && "unable to covert the token_kind_t to unary_op_kind_t");
+    }
+}
+
 static ast_node_t *
 parser_parse_expr_1(parser_t *parser, ast_node_t *lhs, size_t prev_precedence)
 {
@@ -275,6 +296,20 @@ parser_parse_factor(parser_t *parser)
 
             return ast_new_node_ref(parser->arena, token_id.loc, token_id.value);
         }
+        case TOKEN_AND:
+        case TOKEN_STAR:
+        case TOKEN_PLUS:
+        case TOKEN_DASH:
+        case TOKEN_TILDE:
+        case TOKEN_BANG: {
+            ast_node_t *expr = parser_parse_expr(parser);
+            if (expr == NULL) {
+                return NULL;
+            }
+
+            ast_unary_op_kind_t kind = token_kind_to_unary_op_kind(token.kind);
+            return ast_new_node_unary_op(parser->arena, token.loc, kind, expr);
+        }
 
         case TOKEN_OPAREN: {
             ast_node_t *expr = parser_parse_expr(parser);
diff --git a/src/pretty_print_ast.c b/src/pretty_print_ast.c
index edc6320..3a42412 100644
--- a/src/pretty_print_ast.c
+++ b/src/pretty_print_ast.c
@@ -258,7 +258,7 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
             char name[256];
             switch (literal.kind) {
                 case AST_LITERAL_U32: {
-                    sprintf(name, "Literal <kind:u32> <value:%d>", literal.as_u32);
+                    sprintf(name, "Literal <kind:u32> <value:%u>", literal.as_u32);
                     node->name = (char *)arena_alloc(arena, sizeof(char) * (strlen(name) + 1));
                     strcpy(node->name, name);
                     break;
@@ -383,6 +383,26 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
 
             return node;
         }
+
+        case AST_NODE_UNARY_OP: {
+            pretty_print_node_t *node = pretty_print_node_new(arena);
+            ast_unary_op_t unary_op = ast->as_unary_op;
+
+            switch (unary_op.kind) {
+                case AST_UNARY_BITWISE_NOT: {
+                    node->name = "Unary_Operation (~)";
+                    break;
+                }
+                default:
+                    assert(false && "unary operation kind not implemented");
+            }
+
+            pretty_print_node_t *expr = ast_node_to_pretty_print_node(unary_op.expr, arena);
+            list_append(node->children, expr);
+
+            return node;
+        }
+
         default: {
             printf("node kind = '%d' not implmented\n", ast->kind);
             assert(false);
diff --git a/tests/olc/0033_unary_operator_bitwise_not.ol b/tests/olc/0033_unary_operator_bitwise_not.ol
index 1891549..7a9f59d 100644
--- a/tests/olc/0033_unary_operator_bitwise_not.ol
+++ b/tests/olc/0033_unary_operator_bitwise_not.ol
@@ -18,10 +18,22 @@ fn main(): u32 {
   return ~e
 }
 
-# XTEST test_compile(exit_code=0)
+# TEST test_compile(exit_code=0)
 #
-# XTEST test_run_binary(exit_code=0)
+# TEST test_run_binary(exit_code=0)
 #
 # TEST test_contains_tokens WITH
 # ./0033_unary_operator_bitwise_not.ol:18:10: <~>
 # END
+#
+# TEST test_ast WITH
+# Translation_Unit
+# `-Function_Definition <name:main> <return:u32>
+#   `-Block
+#     |-Var_Definition <name:e> <kind:u32>
+#     | `-Literal <kind:u32> <value:4294967295>
+#     `-Return_Statement
+#       `-Unary_Operation (~)
+#         `-Reference <name:e>
+# END
+
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1] docs: info: add while-loop to getting started
@ 2024-10-09 14:53 Johnny Richard
  2024-10-09 12:54 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Johnny Richard @ 2024-10-09 14:53 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

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

diff --git a/docs/info/getting-started.texi b/docs/info/getting-started.texi
index a68f5b1..4cb2e26 100644
--- a/docs/info/getting-started.texi
+++ b/docs/info/getting-started.texi
@@ -78,6 +78,14 @@ if expr {
 }
 @end verbatim
 
+While loop
+
+@verbatim
+while expr {
+  # statement
+}
+@end verbatim
+
 @section Primitive data types
 
 @table @samp

base-commit: 02149b6ddd0bfc6f4e4774eb928e41031889cd73
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang] parser: parse pointer types
@ 2024-10-09 12:19 Carlos Maniero
  2024-10-09 12:19 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-09 12:19 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 src/checker.c              |  2 ++
 src/codegen_linux_x86_64.c |  3 ++
 src/parser.c               | 59 +++++++++++++++++++++++---------------
 src/pretty_print_ast.c     |  2 +-
 src/type.c                 | 14 ++++++++-
 src/type.h                 | 19 ++++++++++--
 6 files changed, 72 insertions(+), 27 deletions(-)

diff --git a/src/checker.c b/src/checker.c
index c688b7a..7a9a7b6 100644
--- a/src/checker.c
+++ b/src/checker.c
@@ -81,6 +81,8 @@ type_resolve(type_t *type)
         case TYPE_UNKNOWN:
             *type = type_from_id(type->as_unknown.id);
             break;
+        case TYPE_PTR:
+            type_resolve(type->as_ptr.type);
         case TYPE_PRIMITIVE:
             break;
     }
diff --git a/src/codegen_linux_x86_64.c b/src/codegen_linux_x86_64.c
index 703f3b5..ac268ad 100644
--- a/src/codegen_linux_x86_64.c
+++ b/src/codegen_linux_x86_64.c
@@ -682,6 +682,9 @@ type_to_bytes(type_t *type)
         case TYPE_PRIMITIVE: {
             return type->as_primitive.size;
         }
+        case TYPE_PTR: {
+            return 8;
+        }
         case TYPE_UNKNOWN: {
             assert(0 && "cannot calculate size of an unknown type: probably a parser issue.");
         }
diff --git a/src/parser.c b/src/parser.c
index 6875b42..0bfa282 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -34,8 +34,8 @@ expected_next_token(parser_t *parser, token_t *token, token_kind_t kind);
 static bool
 expected_token(token_t *token, token_kind_t kind);
 
-static bool
-parser_parse_type(parser_t *parser, string_view_t *type);
+static type_t *
+parser_parse_type(parser_t *parser);
 
 static ast_node_t *
 parser_parse_block(parser_t *parser);
@@ -368,10 +368,13 @@ parser_parse_fn_params(parser_t *parser)
             return NULL;
         }
 
-        string_view_t type_id;
-        parser_parse_type(parser, &type_id);
+        type_t *type = parser_parse_type(parser);
 
-        ast_fn_param_t *param = ast_new_fn_param(parser->arena, token.value, type_new_unknown(parser->arena, type_id));
+        if (type == NULL) {
+            return NULL;
+        }
+
+        ast_fn_param_t *param = ast_new_fn_param(parser->arena, token.value, type);
         list_append(params, param);
 
         skip_line_feeds(parser->lexer);
@@ -408,8 +411,9 @@ parser_parse_fn_definition(parser_t *parser)
         return NULL;
     }
 
-    string_view_t fn_return_type;
-    if (!parser_parse_type(parser, &fn_return_type)) {
+    type_t *ret_type = parser_parse_type(parser);
+
+    if (ret_type == NULL) {
         return NULL;
     }
 
@@ -420,21 +424,16 @@ parser_parse_fn_definition(parser_t *parser)
         return NULL;
     }
 
-    return ast_new_node_fn_def(parser->arena,
-                               fn_name_token.loc,
-                               fn_name_token.value,
-                               params,
-                               type_new_unknown(parser->arena, fn_return_type),
-                               block);
+    return ast_new_node_fn_def(parser->arena, fn_name_token.loc, fn_name_token.value, params, ret_type, block);
 }
 
-static bool
-parser_parse_type(parser_t *parser, string_view_t *type)
+static type_t *
+parser_parse_type(parser_t *parser)
 {
     skip_line_feeds(parser->lexer);
 
     if (!skip_expected_token(parser, TOKEN_COLON)) {
-        return false;
+        return NULL;
     }
 
     skip_line_feeds(parser->lexer);
@@ -442,12 +441,27 @@ parser_parse_type(parser_t *parser, string_view_t *type)
     token_t token;
 
     if (!expected_next_token(parser, &token, TOKEN_ID)) {
-        return false;
+        return NULL;
     }
 
-    *type = token.value;
+    token_t ptr_token;
 
-    return true;
+    lexer_peek_next(parser->lexer, &ptr_token);
+
+    type_t *type = type_new_unknown(parser->arena, token.value);
+
+    if (ptr_token.kind == TOKEN_STAR) {
+        if (!skip_expected_token(parser, TOKEN_STAR)) {
+            return NULL;
+        }
+        string_view_t ptr_id = token.value;
+
+        ptr_id.size = ptr_token.value.chars - token.value.chars + ptr_token.value.size;
+
+        return type_new_ptr(parser->arena, ptr_id, type);
+    }
+
+    return type;
 }
 
 static ast_node_t *
@@ -642,8 +656,8 @@ parser_parse_var_def(parser_t *parser)
         return NULL;
     }
 
-    string_view_t var_type;
-    if (!parser_parse_type(parser, &var_type)) {
+    type_t *type = parser_parse_type(parser);
+    if (type == NULL) {
         return NULL;
     }
 
@@ -657,8 +671,7 @@ parser_parse_var_def(parser_t *parser)
         return NULL;
     }
 
-    ast_node_t *var_node = ast_new_node_var_def(
-        parser->arena, token_id.loc, token_id.value, type_new_unknown(parser->arena, var_type), expr);
+    ast_node_t *var_node = ast_new_node_var_def(parser->arena, token_id.loc, token_id.value, type, expr);
 
     return var_node;
 }
diff --git a/src/pretty_print_ast.c b/src/pretty_print_ast.c
index 5feb270..edc6320 100644
--- a/src/pretty_print_ast.c
+++ b/src/pretty_print_ast.c
@@ -274,7 +274,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.id));
+            sprintf(name, "Var_Definition <name:" SV_FMT "> <kind:" SV_FMT ">", SV_ARG(var.id), SV_ARG(var.type->id));
             node->name = (char *)arena_alloc(arena, sizeof(char) * (strlen(name) + 1));
             strcpy(node->name, name);
 
diff --git a/src/type.c b/src/type.c
index 9de5650..4a8d6f4 100644
--- a/src/type.c
+++ b/src/type.c
@@ -24,6 +24,18 @@ type_new_unknown(arena_t *arena, string_view_t id)
     assert(type);
 
     type->kind = TYPE_UNKNOWN;
-    type->as_unknown.id = id;
+    type->id = id;
+    return type;
+}
+
+type_t *
+type_new_ptr(arena_t *arena, string_view_t id, type_t *ref_type)
+{
+    type_t *type = arena_alloc(arena, sizeof(type_t));
+    assert(type);
+
+    type->kind = TYPE_PTR;
+    type->id = id;
+    type->as_ptr.type = ref_type;
     return type;
 }
diff --git a/src/type.h b/src/type.h
index 97a2a41..d930a88 100644
--- a/src/type.h
+++ b/src/type.h
@@ -18,10 +18,14 @@
 #define TYPE_H
 #include "arena.h"
 #include "string_view.h"
+
+typedef union type type_t;
+
 typedef enum
 {
     TYPE_UNKNOWN,
-    TYPE_PRIMITIVE
+    TYPE_PRIMITIVE,
+    TYPE_PTR
 } type_kind_t;
 
 typedef enum
@@ -46,7 +50,14 @@ typedef struct type_unknown
     string_view_t id;
 } type_unknown_t;
 
-typedef union
+typedef struct type_ptr
+{
+    type_kind_t _type_kind;
+    string_view_t id;
+    type_t *type;
+} type_ptr_t;
+
+typedef union type
 {
     struct
     {
@@ -55,8 +66,12 @@ typedef union
     };
     type_unknown_t as_unknown;
     type_primitive_t as_primitive;
+    type_ptr_t as_ptr;
 } type_t;
 
 type_t *
 type_new_unknown(arena_t *arena, string_view_t id);
+
+type_t *
+type_new_ptr(arena_t *arena, string_view_t id, type_t *type);
 #endif

base-commit: 7f8968454f0aece10c70340f17422d13d14cca79
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1] parser: codegen(x86_64): docs: implement else if
@ 2024-10-09 11:28 Johnny Richard
  2024-10-09  9:29 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Johnny Richard @ 2024-10-09 11:28 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 docs/info/getting-started.texi |  2 +
 docs/info/specification.texi   |  2 +-
 src/codegen_linux_x86_64.c     | 67 ++++++++++++++++++++--------------
 src/parser.c                   |  8 +++-
 tests/olc/0032_else_if.ol      | 50 +++++++++++++++++++++++++
 5 files changed, 99 insertions(+), 30 deletions(-)
 create mode 100644 tests/olc/0032_else_if.ol

diff --git a/docs/info/getting-started.texi b/docs/info/getting-started.texi
index 394b266..a68f5b1 100644
--- a/docs/info/getting-started.texi
+++ b/docs/info/getting-started.texi
@@ -71,6 +71,8 @@ Any non zero expr is true.
 @verbatim
 if expr {
   # statement
+} else if expr {
+  # statement
 } else {
   # statement
 }
diff --git a/docs/info/specification.texi b/docs/info/specification.texi
index 1bac50f..6caaac8 100644
--- a/docs/info/specification.texi
+++ b/docs/info/specification.texi
@@ -42,7 +42,7 @@ language.
 <function-args>       ::= <expression> (<ows> ',' <function-args>)*
 <function-call>       ::= <function-name> <ows> '(' ( <ows> | <ows> <function-args> <ows> ) ')'
 <statement>           ::= <common-statement> | <if-statement> | <while-statement> | <return-statement> | <function-call>
-<if-statement>        ::= 'if' <ws> <expression> <ows> <block>
+<if-statement>        ::= 'if' <ws> <expression> <ows> <block> ( <ows> 'else' ( <ows> <block> | <ows> <if-statement> ) )?
 <while-statement>     ::= 'while' <ws> <expression> <ows> <block>
 <return-statement>    ::= 'return' <ws> <expression>
 
diff --git a/src/codegen_linux_x86_64.c b/src/codegen_linux_x86_64.c
index 5749e65..10005b9 100644
--- a/src/codegen_linux_x86_64.c
+++ b/src/codegen_linux_x86_64.c
@@ -66,6 +66,9 @@ 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 void
+codegen_linux_x86_64_emit_if(codegen_x86_64_t *codegen, ast_if_stmt_t is_stmt);
+
 static void
 codegen_linux_x86_64_put_stack_offset(codegen_x86_64_t *codegen, symbol_t *symbol, size_t offset);
 
@@ -597,34 +600,7 @@ codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
             }
 
             case AST_NODE_IF_STMT: {
-                ast_if_stmt_t if_stmt = node->as_if_stmt;
-
-                ast_node_t *cond = if_stmt.cond;
-                ast_node_t *then = if_stmt.then;
-                ast_node_t *_else = if_stmt._else;
-
-                size_t end_if_label = codegen_linux_x86_64_get_next_label(codegen);
-                size_t end_else_label = codegen_linux_x86_64_get_next_label(codegen);
-
-                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(codegen, &then_block);
-                fprintf(codegen->out, "    jmp .L%ld\n", end_else_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(codegen, &else_block);
-                }
-
-                fprintf(codegen->out, ".L%ld:\n", end_else_label);
-
+                codegen_linux_x86_64_emit_if(codegen, node->as_if_stmt);
                 break;
             }
 
@@ -664,6 +640,41 @@ codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
     codegen->base_offset = block_offset;
 }
 
+static void
+codegen_linux_x86_64_emit_if(codegen_x86_64_t *codegen, ast_if_stmt_t if_stmt)
+{
+    ast_node_t *cond = if_stmt.cond;
+    ast_node_t *then = if_stmt.then;
+    ast_node_t *_else = if_stmt._else;
+
+    size_t end_if_label = codegen_linux_x86_64_get_next_label(codegen);
+    size_t end_else_label = codegen_linux_x86_64_get_next_label(codegen);
+
+    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(codegen, &then_block);
+    fprintf(codegen->out, "    jmp .L%ld\n", end_else_label);
+
+    fprintf(codegen->out, ".L%ld:\n", end_if_label);
+
+    if (_else != NULL) {
+        if (_else->kind == AST_NODE_IF_STMT) {
+            ast_if_stmt_t else_if = _else->as_if_stmt;
+            codegen_linux_x86_64_emit_if(codegen, else_if);
+        } else {
+            ast_block_t else_block = _else->as_block;
+            codegen_linux_x86_64_emit_block(codegen, &else_block);
+        }
+    }
+
+    fprintf(codegen->out, ".L%ld:\n", end_else_label);
+}
+
 static size_t
 type_to_bytes(type_t *type)
 {
diff --git a/src/parser.c b/src/parser.c
index d498ea5..5d6290b 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -573,7 +573,13 @@ parser_parse_if_stmt(parser_t *parser)
         lexer_next_token(parser->lexer, &next_token);
         skip_line_feeds(parser->lexer);
 
-        _else = parser_parse_block(parser);
+        lexer_peek_next(parser->lexer, &next_token);
+
+        if (next_token.kind == TOKEN_IF) {
+            _else = parser_parse_if_stmt(parser);
+        } else {
+            _else = parser_parse_block(parser);
+        }
 
         if (_else == NULL) {
             return NULL;
diff --git a/tests/olc/0032_else_if.ol b/tests/olc/0032_else_if.ol
new file mode 100644
index 0000000..6260838
--- /dev/null
+++ b/tests/olc/0032_else_if.ol
@@ -0,0 +1,50 @@
+# 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
+{
+  if 0 != 0 {
+    return 1
+  } else if 1 == 1 {
+    return 0
+  }
+  return 1
+}
+
+# XTEST test_compile(exit_code=0)
+
+# XTEST test_run_binary(exit_code=0)
+
+# TEST test_ast WITH
+# Translation_Unit
+# `-Function_Definition <name:main> <return:u32>
+#   `-Block
+#     |-If_Statement
+#     | |-Binary_Operation (!=)
+#     | | |-Literal <kind:u32> <value:0>
+#     | | `-Literal <kind:u32> <value:0>
+#     | |-Block
+#     | | `-Return_Statement
+#     | |   `-Literal <kind:u32> <value:1>
+#     | `-If_Statement
+#     |   |-Binary_Operation (==)
+#     |   | |-Literal <kind:u32> <value:1>
+#     |   | `-Literal <kind:u32> <value:1>
+#     |   `-Block
+#     |     `-Return_Statement
+#     |       `-Literal <kind:u32> <value:0>
+#     `-Return_Statement
+#       `-Literal <kind:u32> <value:1>
+# END

base-commit: 55edba3a0a62d33556e3366c49cd7ce5beab6665
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang] parser: returns an unknown type instead of a SV
@ 2024-10-09 11:24 Carlos Maniero
  2024-10-09 11:24 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-09 11:24 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

To support pointer, it is more convenient for the AST to have an
*type_t* structure instead of an string view, once the type_t can
support multiple shapes, such as pointers.

The parser is still not resolving the type which is a checker
responsibility. However, it returns a unknown type which is resolved by
the checker.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 src/ast.c                  |  8 +++---
 src/ast.h                  | 12 ++++----
 src/checker.c              | 59 ++++++++++++++++++++++++++++++++++++--
 src/codegen_linux_x86_64.c | 16 +++++++----
 src/parser.c               | 12 ++++++--
 src/pretty_print_ast.c     |  4 +--
 src/scope.c                |  2 +-
 src/scope.h                |  4 +--
 src/type.c                 | 36 +++++------------------
 src/type.h                 | 25 +++++++++++-----
 10 files changed, 115 insertions(+), 63 deletions(-)

diff --git a/src/ast.c b/src/ast.c
index d6ec70c..79daca6 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -44,7 +44,7 @@ ast_new_node_fn_def(arena_t *arena,
                     token_loc_t loc,
                     string_view_t id,
                     list_t *params,
-                    string_view_t return_type,
+                    type_t *return_type,
                     ast_node_t *block)
 {
     assert(arena);
@@ -86,7 +86,7 @@ ast_new_node_fn_call(arena_t *arena, token_loc_t loc, string_view_t id, list_t *
 }
 
 ast_node_t *
-ast_new_node_var_def(arena_t *arena, token_loc_t loc, string_view_t id, string_view_t type, ast_node_t *value)
+ast_new_node_var_def(arena_t *arena, token_loc_t loc, string_view_t id, 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);
@@ -217,13 +217,13 @@ ast_new_node_block(arena_t *arena)
 }
 
 ast_fn_param_t *
-ast_new_fn_param(arena_t *arena, string_view_t id, string_view_t type_id)
+ast_new_fn_param(arena_t *arena, string_view_t id, type_t *type)
 {
     ast_fn_param_t *fn_param = (ast_fn_param_t *)arena_alloc(arena, sizeof(ast_fn_param_t));
     assert(fn_param);
 
     fn_param->id = id;
-    fn_param->type_id = type_id;
+    fn_param->type = type;
 
     return fn_param;
 }
diff --git a/src/ast.h b/src/ast.h
index 238435e..fccc303 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -66,7 +66,7 @@ typedef struct ast_translation_unit
 typedef struct ast_fn_param
 {
     string_view_t id;
-    string_view_t type_id;
+    type_t *type;
 } ast_fn_param_t;
 
 typedef struct ast_fn_definition
@@ -74,7 +74,7 @@ typedef struct ast_fn_definition
     ast_node_meta_t meta;
     string_view_t id;
     list_t *params;
-    string_view_t return_type;
+    type_t *return_type;
     ast_node_t *block;
     scope_t *scope;
 } ast_fn_definition_t;
@@ -91,7 +91,7 @@ typedef struct ast_var_definition
 {
     ast_node_meta_t meta;
     string_view_t id;
-    string_view_t type;
+    type_t *type;
     ast_node_t *value;
     scope_t *scope;
 } ast_var_definition_t;
@@ -206,14 +206,14 @@ ast_new_node_fn_def(arena_t *arena,
                     token_loc_t loc,
                     string_view_t id,
                     list_t *params,
-                    string_view_t return_type,
+                    type_t *return_type,
                     ast_node_t *block);
 
 ast_node_t *
 ast_new_node_fn_call(arena_t *arena, token_loc_t loc, string_view_t id, list_t *args);
 
 ast_node_t *
-ast_new_node_var_def(arena_t *arena, token_loc_t loc, string_view_t id, string_view_t type, ast_node_t *value);
+ast_new_node_var_def(arena_t *arena, token_loc_t loc, string_view_t id, type_t *type, ast_node_t *value);
 
 ast_node_t *
 ast_new_node_bin_op(arena_t *arena, token_loc_t loc, ast_binary_op_kind_t kind, ast_node_t *lhs, ast_node_t *rhs);
@@ -240,6 +240,6 @@ ast_node_t *
 ast_new_node_block(arena_t *arena);
 
 ast_fn_param_t *
-ast_new_fn_param(arena_t *arena, string_view_t id, string_view_t type_id);
+ast_new_fn_param(arena_t *arena, string_view_t id, type_t *type);
 
 #endif /* AST_H */
diff --git a/src/checker.c b/src/checker.c
index 36202bd..c688b7a 100644
--- a/src/checker.c
+++ b/src/checker.c
@@ -37,6 +37,55 @@ checker_new(arena_t *arena)
     return checker;
 }
 
+static type_t
+type_from_id(string_view_t id)
+{
+    type_t type = { 0 };
+    type.id = id;
+    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");
+}
+
+/**
+ * transform unknown types into actual types
+ */
+static void
+type_resolve(type_t *type)
+{
+    switch (type->kind) {
+        case TYPE_UNKNOWN:
+            *type = type_from_id(type->as_unknown.id);
+            break;
+        case TYPE_PRIMITIVE:
+            break;
+    }
+}
+
 void
 checker_check(checker_t *checker, ast_node_t *ast)
 {
@@ -67,7 +116,8 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
             ast_fn_definition_t *fn_def = &ast->as_fn_def;
             fn_def->scope = scope_push(scope);
 
-            symbol_t *symbol = symbol_new(checker->arena, fn_def->id, type_from_id(fn_def->return_type));
+            type_resolve(fn_def->return_type);
+            symbol_t *symbol = symbol_new(checker->arena, fn_def->id, fn_def->return_type);
             scope_insert(scope, symbol);
 
             list_item_t *item = list_head(fn_def->params);
@@ -75,7 +125,8 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
             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));
+                type_resolve(param->type);
+                symbol_t *symbol = symbol_new(checker->arena, param->id, param->type);
                 scope_insert(fn_def->scope, symbol);
 
                 item = list_next(item);
@@ -156,7 +207,9 @@ 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.id;
 
-            symbol_t *symbol = symbol_new(checker->arena, id, type_from_id(ast->as_var_def.type));
+            type_resolve(ast->as_var_def.type);
+
+            symbol_t *symbol = symbol_new(checker->arena, id, ast->as_var_def.type);
 
             scope_insert(scope, symbol);
             ast->as_var_def.scope = scope;
diff --git a/src/codegen_linux_x86_64.c b/src/codegen_linux_x86_64.c
index 10005b9..703f3b5 100644
--- a/src/codegen_linux_x86_64.c
+++ b/src/codegen_linux_x86_64.c
@@ -165,7 +165,7 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
 
             size_t offset = codegen_linux_x86_64_get_stack_offset(codegen, symbol);
 
-            size_t bytes = type_to_bytes(&symbol->type);
+            size_t bytes = type_to_bytes(symbol->type);
 
             fprintf(codegen->out, "    mov -%ld(%%rbp), %s\n", offset, get_reg_for(REG_ACCUMULATOR, bytes));
             return bytes;
@@ -195,7 +195,7 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
 
             fprintf(codegen->out, "    call " SV_FMT "\n", SV_ARG(fn_call.id));
 
-            return type_to_bytes(&symbol->type);
+            return type_to_bytes(symbol->type);
         }
         case AST_NODE_BINARY_OP: {
             ast_binary_op_t bin_op = expr_node->as_bin_op;
@@ -570,7 +570,7 @@ 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);
                 }
 
-                size_t type_size = type_to_bytes(&symbol->type);
+                size_t type_size = type_to_bytes(symbol->type);
 
                 fprintf(codegen->out,
                         "    mov %s, -%ld(%%rbp)\n",
@@ -593,7 +593,7 @@ codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
 
                 codegen_linux_x86_64_emit_expression(codegen, var_assign.expr);
 
-                size_t type_size = type_to_bytes(&symbol->type);
+                size_t type_size = type_to_bytes(symbol->type);
                 fprintf(codegen->out, "    mov %s, -%ld(%%rbp)\n", get_reg_for(REG_ACCUMULATOR, type_size), offset);
 
                 break;
@@ -682,6 +682,9 @@ type_to_bytes(type_t *type)
         case TYPE_PRIMITIVE: {
             return type->as_primitive.size;
         }
+        case TYPE_UNKNOWN: {
+            assert(0 && "cannot calculate size of an unknown type: probably a parser issue.");
+        }
     }
 
     assert(0 && "unreachable");
@@ -702,7 +705,7 @@ calculate_fn_local_size(scope_t *scope)
 
     for (size_t i = 0; i < scope->symbols->size; ++i) {
         symbol_t *symbol = (symbol_t *)kvs[i]->value;
-        local_size += type_to_bytes(&symbol->type);
+        local_size += type_to_bytes(symbol->type);
     }
 
     size_t max_child_local_size = 0;
@@ -748,7 +751,8 @@ codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen, ast_fn_definition_
 
         fprintf(codegen->out,
                 "    mov %s, -%ld(%%rbp)\n",
-                get_reg_for(x86_call_args[i], symbol->type.as_primitive.size),
+                // FIXME: Type may not be an as_primitive
+                get_reg_for(x86_call_args[i], symbol->type->as_primitive.size),
                 offset);
 
         // FIXME: add offset according to the param size
diff --git a/src/parser.c b/src/parser.c
index 5d6290b..6875b42 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -371,7 +371,7 @@ parser_parse_fn_params(parser_t *parser)
         string_view_t type_id;
         parser_parse_type(parser, &type_id);
 
-        ast_fn_param_t *param = ast_new_fn_param(parser->arena, token.value, type_id);
+        ast_fn_param_t *param = ast_new_fn_param(parser->arena, token.value, type_new_unknown(parser->arena, type_id));
         list_append(params, param);
 
         skip_line_feeds(parser->lexer);
@@ -420,7 +420,12 @@ parser_parse_fn_definition(parser_t *parser)
         return NULL;
     }
 
-    return ast_new_node_fn_def(parser->arena, fn_name_token.loc, fn_name_token.value, params, fn_return_type, block);
+    return ast_new_node_fn_def(parser->arena,
+                               fn_name_token.loc,
+                               fn_name_token.value,
+                               params,
+                               type_new_unknown(parser->arena, fn_return_type),
+                               block);
 }
 
 static bool
@@ -652,7 +657,8 @@ parser_parse_var_def(parser_t *parser)
         return NULL;
     }
 
-    ast_node_t *var_node = ast_new_node_var_def(parser->arena, token_id.loc, token_id.value, var_type, expr);
+    ast_node_t *var_node = ast_new_node_var_def(
+        parser->arena, token_id.loc, token_id.value, type_new_unknown(parser->arena, var_type), expr);
 
     return var_node;
 }
diff --git a/src/pretty_print_ast.c b/src/pretty_print_ast.c
index 0c5d2d5..5feb270 100644
--- a/src/pretty_print_ast.c
+++ b/src/pretty_print_ast.c
@@ -113,7 +113,7 @@ pretty_print_new_fn_param(ast_fn_param_t *param, arena_t *arena)
 {
     pretty_print_node_t *node = pretty_print_node_new(arena);
     char name[256];
-    sprintf(name, "Param_Definition <name:" SV_FMT "> <type:" SV_FMT ">", SV_ARG(param->id), SV_ARG(param->type_id));
+    sprintf(name, "Param_Definition <name:" SV_FMT "> <type:" SV_FMT ">", SV_ARG(param->id), SV_ARG(param->type->id));
     node->name = (char *)arena_alloc(arena, sizeof(char) * (strlen(name) + 1));
     strcpy(node->name, name);
     return node;
@@ -148,7 +148,7 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
             sprintf(name,
                     "Function_Definition <name:" SV_FMT "> <return:" SV_FMT ">",
                     SV_ARG(fn_def.id),
-                    SV_ARG(fn_def.return_type));
+                    SV_ARG(fn_def.return_type->id));
             node->name = (char *)arena_alloc(arena, sizeof(char) * (strlen(name) + 1));
             strcpy(node->name, name);
 
diff --git a/src/scope.c b/src/scope.c
index 81f610a..e483fbe 100644
--- a/src/scope.c
+++ b/src/scope.c
@@ -49,7 +49,7 @@ scope_new(arena_t *arena)
 }
 
 symbol_t *
-symbol_new(arena_t *arena, string_view_t id, type_t type)
+symbol_new(arena_t *arena, string_view_t id, type_t *type)
 {
     assert(arena);
     symbol_t *symbol = (symbol_t *)arena_alloc(arena, sizeof(symbol_t));
diff --git a/src/scope.h b/src/scope.h
index 92a5a4f..08eb681 100644
--- a/src/scope.h
+++ b/src/scope.h
@@ -26,7 +26,7 @@
 typedef struct symbol
 {
     string_view_t id;
-    type_t type;
+    type_t *type;
 } symbol_t;
 
 typedef struct scope
@@ -41,7 +41,7 @@ scope_t *
 scope_new(arena_t *arena);
 
 symbol_t *
-symbol_new(arena_t *arena, string_view_t id, type_t type);
+symbol_new(arena_t *arena, string_view_t id, type_t *type);
 
 symbol_t *
 scope_lookup(scope_t *scope, string_view_t id);
diff --git a/src/type.c b/src/type.c
index 64147a2..9de5650 100644
--- a/src/type.c
+++ b/src/type.c
@@ -17,35 +17,13 @@
 #include "type.h"
 #include "assert.h"
 
-type_t
-type_from_id(string_view_t id)
+type_t *
+type_new_unknown(arena_t *arena, 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;
-    }
+    type_t *type = arena_alloc(arena, sizeof(type_t));
+    assert(type);
 
-    // FIXME: handle user defined types
-    assert(0 && "unknown type");
+    type->kind = TYPE_UNKNOWN;
+    type->as_unknown.id = id;
+    return type;
 }
diff --git a/src/type.h b/src/type.h
index 1da3a11..97a2a41 100644
--- a/src/type.h
+++ b/src/type.h
@@ -16,9 +16,11 @@
  */
 #ifndef TYPE_H
 #define TYPE_H
+#include "arena.h"
 #include "string_view.h"
 typedef enum
 {
+    TYPE_UNKNOWN,
     TYPE_PRIMITIVE
 } type_kind_t;
 
@@ -32,20 +34,29 @@ typedef enum
 
 typedef struct type_primitive
 {
-    short size;
+    type_kind_t _type_kind;
+    string_view_t id;
     type_primitive_kind_t kind;
+    short size;
 } type_primitive_t;
 
-typedef struct type
+typedef struct type_unknown
 {
+    type_kind_t _type_kind;
     string_view_t id;
-    type_kind_t kind;
-    union
+} type_unknown_t;
+
+typedef union
+{
+    struct
     {
-        type_primitive_t as_primitive;
+        type_kind_t kind;
+        string_view_t id;
     };
+    type_unknown_t as_unknown;
+    type_primitive_t as_primitive;
 } type_t;
 
-type_t
-type_from_id(string_view_t id);
+type_t *
+type_new_unknown(arena_t *arena, string_view_t id);
 #endif

base-commit: 2407c8150403c373df86e66b5886102c9f143193
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1] parser: add support for parsing while-statements
@ 2024-10-08 21:33 Johnny Richard
  2024-10-08 19:34 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Johnny Richard @ 2024-10-08 21:33 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/ast.c                         | 14 +++++++++++
 src/ast.h                         | 12 ++++++++++
 src/checker.c                     |  7 ++++++
 src/codegen_linux_x86_64.c        | 25 ++++++++++++++++++++
 src/parser.c                      | 39 +++++++++++++++++++++++++++++++
 src/pretty_print_ast.c            | 14 +++++++++++
 tests/olc/0030_while_statement.ol | 20 ++++++++++++----
 7 files changed, 127 insertions(+), 4 deletions(-)

diff --git a/src/ast.c b/src/ast.c
index 531415c..d6ec70c 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -186,6 +186,20 @@ ast_new_node_if_stmt(arena_t *arena, token_loc_t loc, ast_node_t *cond, ast_node
     return node_if_stmt;
 }
 
+ast_node_t *
+ast_new_node_while_stmt(arena_t *arena, token_loc_t loc, ast_node_t *cond, ast_node_t *then)
+{
+    ast_node_t *node_while_stmt = arena_alloc(arena, sizeof(ast_node_t));
+    assert(node_while_stmt);
+
+    node_while_stmt->kind = AST_NODE_WHILE_STMT;
+    node_while_stmt->loc = loc;
+    node_while_stmt->as_while_stmt.cond = cond;
+    node_while_stmt->as_while_stmt.then = then;
+
+    return node_while_stmt;
+}
+
 ast_node_t *
 ast_new_node_block(arena_t *arena)
 {
diff --git a/src/ast.h b/src/ast.h
index dce7cd8..238435e 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -39,6 +39,7 @@ typedef enum
     AST_NODE_VAR_ASSIGN_STMT,
     AST_NODE_RETURN_STMT,
     AST_NODE_IF_STMT,
+    AST_NODE_WHILE_STMT,
     AST_NODE_LITERAL,
     AST_NODE_REF,
     AST_NODE_UNKNOWN
@@ -168,6 +169,13 @@ typedef struct ast_if_stmt
     ast_node_t *_else;
 } ast_if_stmt_t;
 
+typedef struct ast_while_stmt
+{
+    ast_node_meta_t meta;
+    ast_node_t *cond;
+    ast_node_t *then;
+} ast_while_stmt_t;
+
 typedef union ast_node
 {
     // inlined ast_node_meta_t struct.
@@ -187,6 +195,7 @@ typedef union ast_node
     ast_var_assign_stmt_t as_var_assign_stmt;
     ast_return_stmt_t as_return_stmt;
     ast_if_stmt_t as_if_stmt;
+    ast_while_stmt_t as_while_stmt;
 } ast_node_t;
 
 ast_node_t *
@@ -224,6 +233,9 @@ ast_new_node_return_stmt(arena_t *arena, token_loc_t loc, ast_node_t *expr);
 ast_node_t *
 ast_new_node_if_stmt(arena_t *arena, token_loc_t loc, ast_node_t *cond, ast_node_t *then, ast_node_t *_else);
 
+ast_node_t *
+ast_new_node_while_stmt(arena_t *arena, token_loc_t loc, ast_node_t *cond, ast_node_t *then);
+
 ast_node_t *
 ast_new_node_block(arena_t *arena);
 
diff --git a/src/checker.c b/src/checker.c
index 4523b79..36202bd 100644
--- a/src/checker.c
+++ b/src/checker.c
@@ -109,6 +109,13 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
             return;
         }
 
+        case AST_NODE_WHILE_STMT: {
+            populate_scope(checker, scope, ast->as_while_stmt.cond);
+            populate_scope(checker, scope, ast->as_while_stmt.then);
+
+            return;
+        }
+
         case AST_NODE_BINARY_OP: {
             ast_binary_op_t bin_op = ast->as_bin_op;
 
diff --git a/src/codegen_linux_x86_64.c b/src/codegen_linux_x86_64.c
index ebda8eb..5749e65 100644
--- a/src/codegen_linux_x86_64.c
+++ b/src/codegen_linux_x86_64.c
@@ -627,6 +627,31 @@ codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
 
                 break;
             }
+
+            case AST_NODE_WHILE_STMT: {
+                ast_while_stmt_t while_stmt = node->as_while_stmt;
+
+                ast_node_t *cond = while_stmt.cond;
+                ast_node_t *then = while_stmt.then;
+
+                size_t begin_label = codegen_linux_x86_64_get_next_label(codegen);
+                size_t end_label = codegen_linux_x86_64_get_next_label(codegen);
+
+                fprintf(codegen->out, ".L%ld:\n", begin_label);
+                codegen_linux_x86_64_emit_expression(codegen, cond);
+                fprintf(codegen->out, "    cmp $1, %%rax\n");
+                fprintf(codegen->out, "    jnz .L%ld\n", end_label);
+
+                assert(then->kind == AST_NODE_BLOCK && "invalid while-then block");
+                ast_block_t then_block = then->as_block;
+
+                codegen_linux_x86_64_emit_block(codegen, &then_block);
+
+                fprintf(codegen->out, "    jmp .L%ld\n", begin_label);
+                fprintf(codegen->out, ".L%ld:\n", end_label);
+
+                break;
+            }
             default: {
                 // FIXME: improve error: replace the node->kind to a string representation
                 fprintf(stderr, "node kind %d not supported\n", node->kind);
diff --git a/src/parser.c b/src/parser.c
index 94e8a1e..d498ea5 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -46,6 +46,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_while_stmt(parser_t *parser);
+
 static ast_node_t *
 parser_parse_var_def(parser_t *parser);
 
@@ -471,6 +474,10 @@ StartLoop:
             node = parser_parse_if_stmt(parser);
             break;
         }
+        case TOKEN_WHILE: {
+            node = parser_parse_while_stmt(parser);
+            break;
+        }
         case TOKEN_VAR: {
             node = parser_parse_var_def(parser);
             break;
@@ -580,6 +587,38 @@ parser_parse_if_stmt(parser_t *parser)
     return node_if_stmt;
 }
 
+static ast_node_t *
+parser_parse_while_stmt(parser_t *parser)
+{
+    token_t token_while;
+    if (!expected_next_token(parser, &token_while, TOKEN_WHILE)) {
+        return NULL;
+    }
+
+    ast_node_t *cond = parser_parse_expr(parser);
+
+    if (cond == NULL) {
+        return NULL;
+    }
+
+    skip_line_feeds(parser->lexer);
+
+    ast_node_t *then = parser_parse_block(parser);
+
+    if (then == NULL) {
+        return NULL;
+    }
+
+    token_t next_token;
+    peek_next_non_lf_token(parser->lexer, &next_token);
+
+    ast_node_t *node_while_stmt = ast_new_node_while_stmt(parser->arena, token_while.loc, cond, then);
+
+    assert(node_while_stmt);
+
+    return node_while_stmt;
+}
+
 static ast_node_t *
 parser_parse_var_def(parser_t *parser)
 {
diff --git a/src/pretty_print_ast.c b/src/pretty_print_ast.c
index 3282166..0c5d2d5 100644
--- a/src/pretty_print_ast.c
+++ b/src/pretty_print_ast.c
@@ -237,6 +237,20 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
 
             return node;
         }
+        case AST_NODE_WHILE_STMT: {
+            pretty_print_node_t *node = pretty_print_node_new(arena);
+            ast_while_stmt_t while_stmt = ast->as_while_stmt;
+
+            node->name = "While_Statement";
+
+            pretty_print_node_t *child = ast_node_to_pretty_print_node(while_stmt.cond, arena);
+            list_append(node->children, child);
+
+            child = ast_node_to_pretty_print_node(while_stmt.then, arena);
+            list_append(node->children, child);
+
+            return node;
+        }
         case AST_NODE_LITERAL: {
             pretty_print_node_t *node = pretty_print_node_new(arena);
             ast_literal_t literal = ast->as_literal;
diff --git a/tests/olc/0030_while_statement.ol b/tests/olc/0030_while_statement.ol
index 1ebdc5a..0eca528 100644
--- a/tests/olc/0030_while_statement.ol
+++ b/tests/olc/0030_while_statement.ol
@@ -23,16 +23,28 @@ fn main(): u32 {
   return i
 }
 
-# XTEST test_compile(exit_code=0)
+# TEST test_compile(exit_code=0)
 #
-# XTEST test_run_binary(exit_code=9)
+# TEST test_run_binary(exit_code=10)
 #
-# XTEST test_ast WITH
+# TEST test_ast WITH
 # Translation_Unit
 # `-Function_Definition <name:main> <return:u32>
 #   `-Block
+#     |-Var_Definition <name:i> <kind:u32>
+#     | `-Literal <kind:u32> <value:0>
+#     |-While_Statement
+#     | |-Binary_Operation (<)
+#     | | |-Reference <name:i>
+#     | | `-Literal <kind:u32> <value:10>
+#     | `-Block
+#     |   `-Var_Assigment
+#     |     |-Reference <name:i>
+#     |     `-Binary_Operation (+)
+#     |       |-Reference <name:i>
+#     |       `-Literal <kind:u32> <value:1>
 #     `-Return_Statement
-#       `-Literal <kind:u32> <value:0>
+#       `-Reference <name:i>
 # END
 #
 # TEST test_contains_tokens WITH

base-commit: f0a565d7d551df220c7b8ae0b924245dd70c9468
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1 2/2] chore: ignore *.orig file on root .gitignore
@ 2024-10-08 16:33 Johnny Richard
  2024-10-08 14:35 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Johnny Richard @ 2024-10-08 16:33 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 .gitignore | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.gitignore b/.gitignore
index f18560c..54f5c40 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,5 +2,6 @@
 build
 *.o
 *.info
+*.orig
 docs/site.tar.gz
 tests/*/*_test
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1] lexer: add 'while' keyword token
@ 2024-10-08 15:53 Johnny Richard
  2024-10-08 13:54 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Johnny Richard @ 2024-10-08 15:53 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 docs/info/specification.texi      |  3 ++-
 src/lexer.c                       |  5 ++++
 src/lexer.h                       |  1 +
 tests/olc/0030_while_statement.ol | 40 +++++++++++++++++++++++++++++++
 4 files changed, 48 insertions(+), 1 deletion(-)
 create mode 100644 tests/olc/0030_while_statement.ol

diff --git a/docs/info/specification.texi b/docs/info/specification.texi
index 9144133..1bac50f 100644
--- a/docs/info/specification.texi
+++ b/docs/info/specification.texi
@@ -41,8 +41,9 @@ language.
 <block>               ::= '{' <ows> <statement> <ows> (<end-of-statement> <ows> <statement> <ows>)* <end-of-statement>? <ows> '}'
 <function-args>       ::= <expression> (<ows> ',' <function-args>)*
 <function-call>       ::= <function-name> <ows> '(' ( <ows> | <ows> <function-args> <ows> ) ')'
-<statement>           ::= <common-statement> | <if-statement> | <return-statement> | <function-call>
+<statement>           ::= <common-statement> | <if-statement> | <while-statement> | <return-statement> | <function-call>
 <if-statement>        ::= 'if' <ws> <expression> <ows> <block>
+<while-statement>     ::= 'while' <ws> <expression> <ows> <block>
 <return-statement>    ::= 'return' <ws> <expression>
 
 (* Statements *)
diff --git a/src/lexer.c b/src/lexer.c
index 4784f1c..5b4bbaa 100644
--- a/src/lexer.c
+++ b/src/lexer.c
@@ -290,6 +290,7 @@ static char *token_kind_str_table[] = {
     [TOKEN_RETURN] = "return",
     [TOKEN_IF] = "if",
     [TOKEN_ELSE] = "else",
+    [TOKEN_WHILE] = "while",
     [TOKEN_VAR] = "var",
     [TOKEN_LF] = "line_feed",
     [TOKEN_OPAREN] = "(",
@@ -424,6 +425,10 @@ lexer_str_to_token_kind(string_view_t text)
         return TOKEN_ELSE;
     }
 
+    if (string_view_eq_to_cstr(text, "while")) {
+        return TOKEN_WHILE;
+    }
+
     if (string_view_eq_to_cstr(text, "var")) {
         return TOKEN_VAR;
     }
diff --git a/src/lexer.h b/src/lexer.h
index bb39a70..8beaea8 100644
--- a/src/lexer.h
+++ b/src/lexer.h
@@ -51,6 +51,7 @@ typedef enum token_kind
     TOKEN_RETURN,
     TOKEN_IF,
     TOKEN_ELSE,
+    TOKEN_WHILE,
     TOKEN_VAR,
 
     // Equality operators
diff --git a/tests/olc/0030_while_statement.ol b/tests/olc/0030_while_statement.ol
new file mode 100644
index 0000000..eb47ae5
--- /dev/null
+++ b/tests/olc/0030_while_statement.ol
@@ -0,0 +1,40 @@
+# 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 i: u32 = 0
+
+  while i < 10 {
+    i = i + 1
+  }
+
+  return i 
+}
+
+# XTEST test_compile(exit_code=0)
+#
+# XTEST test_run_binary(exit_code=9)
+#
+# XTEST test_ast WITH
+# Translation_Unit
+# `-Function_Definition <name:main> <return:u32>
+#   `-Block
+#     `-Return_Statement
+#       `-Literal <kind:u32> <value:0>
+# END
+#
+# TEST test_contains_tokens WITH
+# ./0030_while_statement.ol:19:3: <while>
+# END

base-commit: dbbfdf2acab2b022cc9f0157d4341478d82b8838
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1] codegen: x86_64: emit assembly for var assignment stmts
@ 2024-10-08 14:49 Johnny Richard
  2024-10-08 12:50 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Johnny Richard @ 2024-10-08 14:49 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Since the codegen for var assignment also fetch offset on stack_offset
map and this logic is spread everywhere, I've introduced two new
functions to 'put' and 'get' stack_offsets to reduce code duplication.

I also reduced the key string key size for stack_offset by replacing
'%p' to '%lx' once the second option don't add an extra '0x' prefix.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/codegen_linux_x86_64.c      | 74 +++++++++++++++++++++++----------
 tests/olc/0029_var_assigment.ol |  4 +-
 2 files changed, 55 insertions(+), 23 deletions(-)

diff --git a/src/codegen_linux_x86_64.c b/src/codegen_linux_x86_64.c
index e501634..ebda8eb 100644
--- a/src/codegen_linux_x86_64.c
+++ b/src/codegen_linux_x86_64.c
@@ -24,7 +24,7 @@
 #include "scope.h"
 
 #define SYS_exit (60)
-#define PTR_HEX_CSTR_SIZE (18 + 1)
+#define PTR_HEX_CSTR_SIZE (16 + 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.
@@ -66,6 +66,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 void
+codegen_linux_x86_64_put_stack_offset(codegen_x86_64_t *codegen, symbol_t *symbol, size_t offset);
+
+static size_t
+codegen_linux_x86_64_get_stack_offset(codegen_x86_64_t *codegen, symbol_t *symbol);
+
 static size_t
 type_to_bytes(type_t *type);
 
@@ -154,15 +160,11 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
             symbol_t *symbol = scope_lookup(ref.scope, ref.id);
             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);
+            size_t offset = codegen_linux_x86_64_get_stack_offset(codegen, symbol);
 
             size_t bytes = type_to_bytes(&symbol->type);
 
-            fprintf(codegen->out, "    mov -%ld(%%rbp), %s\n", *offset, get_reg_for(REG_ACCUMULATOR, bytes));
+            fprintf(codegen->out, "    mov -%ld(%%rbp), %s\n", offset, get_reg_for(REG_ACCUMULATOR, bytes));
             return bytes;
         }
         case AST_NODE_FN_CALL: {
@@ -559,18 +561,12 @@ codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
                 symbol_t *symbol = scope_lookup(scope, var_def.id);
                 assert(symbol);
 
-                char symbol_ptr[PTR_HEX_CSTR_SIZE];
-                sprintf(symbol_ptr, "%p", (void *)symbol);
+                codegen_linux_x86_64_put_stack_offset(codegen, symbol, codegen->base_offset);
 
                 if (var_def.value) {
                     codegen_linux_x86_64_emit_expression(codegen, var_def.value);
                 }
 
-                size_t *offset = arena_alloc(codegen->arena, sizeof(size_t));
-                *offset = codegen->base_offset;
-
-                map_put(codegen->symbols_stack_offset, symbol_ptr, offset);
-
                 size_t type_size = type_to_bytes(&symbol->type);
 
                 fprintf(codegen->out,
@@ -582,6 +578,24 @@ codegen_linux_x86_64_emit_block(codegen_x86_64_t *codegen, ast_block_t *block)
                 break;
             }
 
+            case AST_NODE_VAR_ASSIGN_STMT: {
+                ast_var_assign_stmt_t var_assign = node->as_var_assign_stmt;
+                ast_ref_t ref = var_assign.ref->as_ref;
+                scope_t *scope = ref.scope;
+
+                symbol_t *symbol = scope_lookup(scope, ref.id);
+                assert(symbol);
+
+                size_t offset = codegen_linux_x86_64_get_stack_offset(codegen, symbol);
+
+                codegen_linux_x86_64_emit_expression(codegen, var_assign.expr);
+
+                size_t type_size = type_to_bytes(&symbol->type);
+                fprintf(codegen->out, "    mov %s, -%ld(%%rbp)\n", get_reg_for(REG_ACCUMULATOR, type_size), offset);
+
+                break;
+            }
+
             case AST_NODE_IF_STMT: {
                 ast_if_stmt_t if_stmt = node->as_if_stmt;
 
@@ -689,21 +703,17 @@ codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen, ast_fn_definition_
 
         ast_fn_param_t *param = item->value;
 
-        size_t *offset = arena_alloc(codegen->arena, sizeof(size_t));
-        *offset = codegen->base_offset;
-
         symbol_t *symbol = scope_lookup(fn_def->scope, param->id);
         assert(symbol);
 
-        char symbol_ptr[PTR_HEX_CSTR_SIZE];
-        sprintf(symbol_ptr, "%p", (void *)symbol);
+        size_t offset = codegen->base_offset;
 
-        map_put(codegen->symbols_stack_offset, symbol_ptr, offset);
+        codegen_linux_x86_64_put_stack_offset(codegen, symbol, codegen->base_offset);
 
         fprintf(codegen->out,
                 "    mov %s, -%ld(%%rbp)\n",
                 get_reg_for(x86_call_args[i], symbol->type.as_primitive.size),
-                *offset);
+                offset);
 
         // FIXME: add offset according to the param size
         codegen->base_offset += 8;
@@ -722,6 +732,28 @@ codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen, ast_fn_definition_
     codegen_linux_x86_64_emit_block(codegen, &block);
 }
 
+static void
+codegen_linux_x86_64_put_stack_offset(codegen_x86_64_t *codegen, symbol_t *symbol, size_t offset)
+{
+
+    size_t *stack_offset = arena_alloc(codegen->arena, sizeof(size_t));
+    *stack_offset = offset;
+
+    char symbol_ptr[PTR_HEX_CSTR_SIZE];
+    sprintf(symbol_ptr, "%lx", (uintptr_t)symbol);
+
+    map_put(codegen->symbols_stack_offset, symbol_ptr, stack_offset);
+}
+
+static size_t
+codegen_linux_x86_64_get_stack_offset(codegen_x86_64_t *codegen, symbol_t *symbol)
+{
+    char symbol_ptr[PTR_HEX_CSTR_SIZE];
+    sprintf(symbol_ptr, "%lx", (uintptr_t)symbol);
+
+    return *(size_t *)map_get(codegen->symbols_stack_offset, symbol_ptr);
+}
+
 static char *
 get_reg_for(x86_64_register_type_t type, size_t bytes)
 {
diff --git a/tests/olc/0029_var_assigment.ol b/tests/olc/0029_var_assigment.ol
index 6c5c46f..0e04fcc 100644
--- a/tests/olc/0029_var_assigment.ol
+++ b/tests/olc/0029_var_assigment.ol
@@ -19,9 +19,9 @@ fn main(): u32 {
   return code
 }
 
-# XTEST test_compile(exit_code=0)
+# TEST test_compile(exit_code=0)
 
-# XTEST test_run_binary(exit_code=0)
+# TEST test_run_binary(exit_code=0)
 
 # TEST test_ast WITH
 # Translation_Unit

base-commit: dbbfdf2acab2b022cc9f0157d4341478d82b8838
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1] parser: add support for parsing var assignments
@ 2024-10-08 13:37 Johnny Richard
  2024-10-08 11:38 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Johnny Richard @ 2024-10-08 13:37 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/ast.c                       | 14 +++++++++++++
 src/ast.h                       | 12 +++++++++++
 src/checker.c                   |  8 +++++++
 src/parser.c                    | 37 ++++++++++++++++++++++++++++++++-
 src/pretty_print_ast.c          | 14 +++++++++++++
 tests/olc/0029_var_assigment.ol | 37 +++++++++++++++++++++++++++++++++
 6 files changed, 121 insertions(+), 1 deletion(-)
 create mode 100644 tests/olc/0029_var_assigment.ol

diff --git a/src/ast.c b/src/ast.c
index 1224305..531415c 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -144,6 +144,20 @@ ast_new_node_ref(arena_t *arena, token_loc_t loc, string_view_t id)
     return node_ref;
 }
 
+ast_node_t *
+ast_new_node_var_assign_stmt(arena_t *arena, token_loc_t loc, ast_node_t *ref, ast_node_t *expr)
+{
+    ast_node_t *node_var_assign_stmt = (ast_node_t *)arena_alloc(arena, sizeof(ast_node_t));
+    assert(node_var_assign_stmt);
+
+    node_var_assign_stmt->kind = AST_NODE_VAR_ASSIGN_STMT;
+    node_var_assign_stmt->loc = loc;
+    node_var_assign_stmt->as_var_assign_stmt.ref = ref;
+    node_var_assign_stmt->as_var_assign_stmt.expr = expr;
+
+    return node_var_assign_stmt;
+}
+
 ast_node_t *
 ast_new_node_return_stmt(arena_t *arena, token_loc_t loc, ast_node_t *expr)
 {
diff --git a/src/ast.h b/src/ast.h
index f9a23b5..dce7cd8 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -36,6 +36,7 @@ typedef enum
     AST_NODE_FN_CALL,
     AST_NODE_VAR_DEF,
     AST_NODE_BINARY_OP,
+    AST_NODE_VAR_ASSIGN_STMT,
     AST_NODE_RETURN_STMT,
     AST_NODE_IF_STMT,
     AST_NODE_LITERAL,
@@ -146,6 +147,13 @@ typedef struct ast_binary_op
     ast_node_t *rhs;
 } ast_binary_op_t;
 
+typedef struct ast_var_assign_stmt
+{
+    ast_node_meta_t meta;
+    ast_node_t *ref;
+    ast_node_t *expr;
+} ast_var_assign_stmt_t;
+
 typedef struct ast_return_stmt
 {
     ast_node_meta_t meta;
@@ -176,6 +184,7 @@ typedef union ast_node
     ast_literal_t as_literal;
     ast_ref_t as_ref;
     ast_block_t as_block;
+    ast_var_assign_stmt_t as_var_assign_stmt;
     ast_return_stmt_t as_return_stmt;
     ast_if_stmt_t as_if_stmt;
 } ast_node_t;
@@ -206,6 +215,9 @@ ast_new_node_literal_u32(arena_t *arena, token_loc_t loc, uint32_t value);
 ast_node_t *
 ast_new_node_ref(arena_t *arena, token_loc_t loc, string_view_t id);
 
+ast_node_t *
+ast_new_node_var_assign_stmt(arena_t *arena, token_loc_t loc, ast_node_t *ref, ast_node_t *expr);
+
 ast_node_t *
 ast_new_node_return_stmt(arena_t *arena, token_loc_t loc, ast_node_t *expr);
 
diff --git a/src/checker.c b/src/checker.c
index ac4102a..4523b79 100644
--- a/src/checker.c
+++ b/src/checker.c
@@ -117,6 +117,14 @@ populate_scope(checker_t *checker, scope_t *scope, ast_node_t *ast)
             return;
         }
 
+        case AST_NODE_VAR_ASSIGN_STMT: {
+            ast_var_assign_stmt_t var_assign_stmt = ast->as_var_assign_stmt;
+
+            populate_scope(checker, scope, var_assign_stmt.ref);
+            populate_scope(checker, scope, var_assign_stmt.expr);
+            return;
+        }
+
         case AST_NODE_RETURN_STMT: {
             ast_return_stmt_t return_stmt = ast->as_return_stmt;
 
diff --git a/src/parser.c b/src/parser.c
index 35c8107..d16b79d 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -49,6 +49,9 @@ parser_parse_if_stmt(parser_t *parser);
 static ast_node_t *
 parser_parse_var_def(parser_t *parser);
 
+static ast_node_t *
+parser_parse_var_assign_stmt(parser_t *parser);
+
 static ast_node_t *
 parser_parse_fn_definition(parser_t *parser);
 
@@ -469,6 +472,14 @@ StartLoop:
             node = parser_parse_var_def(parser);
             break;
         }
+        case TOKEN_ID: {
+            lexer_lookahead(parser->lexer, &next_token, 2);
+            if (!expected_token(&next_token, TOKEN_EQ)) {
+                return NULL;
+            }
+            node = parser_parse_var_assign_stmt(parser);
+            break;
+        }
         case TOKEN_CCURLY: {
             goto EndLoop;
         }
@@ -487,7 +498,6 @@ StartLoop:
     goto StartLoop;
 EndLoop:
 
-    skip_line_feeds(parser->lexer);
     if (!skip_expected_token(parser, TOKEN_CCURLY)) {
         return NULL;
     }
@@ -603,6 +613,31 @@ parser_parse_var_def(parser_t *parser)
     return var_node;
 }
 
+static ast_node_t *
+parser_parse_var_assign_stmt(parser_t *parser)
+{
+    token_t token_id;
+
+    if (!expected_next_token(parser, &token_id, TOKEN_ID)) {
+        return NULL;
+    }
+
+    token_t token_eq;
+
+    if (!expected_next_token(parser, &token_eq, TOKEN_EQ)) {
+        return NULL;
+    }
+
+    ast_node_t *ref = ast_new_node_ref(parser->arena, token_id.loc, token_id.value);
+    ast_node_t *expr = parser_parse_expr(parser);
+
+    // FIXME: The expected line feed should be parsed from parent call
+    //        according to the grammar rules
+    skip_line_feeds(parser->lexer);
+
+    return ast_new_node_var_assign_stmt(parser->arena, token_eq.loc, ref, expr);
+}
+
 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 2541544..3282166 100644
--- a/src/pretty_print_ast.c
+++ b/src/pretty_print_ast.c
@@ -193,6 +193,20 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
             }
             return node;
         }
+        case AST_NODE_VAR_ASSIGN_STMT: {
+            pretty_print_node_t *node = pretty_print_node_new(arena);
+            ast_var_assign_stmt_t var_assign_stmt = ast->as_var_assign_stmt;
+
+            node->name = "Var_Assigment";
+
+            pretty_print_node_t *ref = ast_node_to_pretty_print_node(var_assign_stmt.ref, arena);
+            pretty_print_node_t *expr = ast_node_to_pretty_print_node(var_assign_stmt.expr, arena);
+
+            list_append(node->children, ref);
+            list_append(node->children, expr);
+
+            return node;
+        }
         case AST_NODE_RETURN_STMT: {
             pretty_print_node_t *node = pretty_print_node_new(arena);
             ast_return_stmt_t return_stmt = ast->as_return_stmt;
diff --git a/tests/olc/0029_var_assigment.ol b/tests/olc/0029_var_assigment.ol
new file mode 100644
index 0000000..6c5c46f
--- /dev/null
+++ b/tests/olc/0029_var_assigment.ol
@@ -0,0 +1,37 @@
+# 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 code: u32 = 1
+  code = 0
+  return code
+}
+
+# XTEST test_compile(exit_code=0)
+
+# XTEST test_run_binary(exit_code=0)
+
+# TEST test_ast WITH
+# Translation_Unit
+# `-Function_Definition <name:main> <return:u32>
+#   `-Block
+#     |-Var_Definition <name:code> <kind:u32>
+#     | `-Literal <kind:u32> <value:1>
+#     |-Var_Assigment
+#     | |-Reference <name:code>
+#     | `-Literal <kind:u32> <value:0>
+#     `-Return_Statement
+#       `-Reference <name:code>
+# END

base-commit: f493e84234cc21dafc987f07be5a5f7dea9e8c60
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang] docs: fix remove semicolumn on the example
@ 2024-10-08 10:01 Carlos Maniero
  2024-10-08 10:01 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-08 10:01 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 docs/info/getting-started.texi | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/info/getting-started.texi b/docs/info/getting-started.texi
index e9ff5d3..394b266 100644
--- a/docs/info/getting-started.texi
+++ b/docs/info/getting-started.texi
@@ -15,7 +15,7 @@ function must return the program exit code.
 
 @verbatim
 fn main(): u8 {
-  return 0;
+  return 0
 }
 @end verbatim
 

base-commit: 66e9ecf77bf2a1467a79b2217d61bb420f3e7b22
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang] docs: introduce olang for newcomers
@ 2024-10-08  3:15 Carlos Maniero
  2024-10-08  3:15 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-08  3:15 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

The section introduction and getting started was empty in our manual.
Even that this is not the final documentation, this is something we can
iterate over to provide a basic O programming language overview for
newcomers.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 docs/info/getting-started.texi | 229 +++++++++++++++++++++++++++++++++
 docs/info/introduction.texi    |   6 +
 2 files changed, 235 insertions(+)

diff --git a/docs/info/getting-started.texi b/docs/info/getting-started.texi
index e94f124..e9ff5d3 100644
--- a/docs/info/getting-started.texi
+++ b/docs/info/getting-started.texi
@@ -1,2 +1,231 @@
 @node Getting Started
 @chapter Getting Started
+
+Welcome to the O programming language! This chapter will introduce you to the
+basics of the language's syntax. We'll cover variables, data types, operators,
+control flow, and functions.
+
+By the end of this chapter, you'll have a solid foundation in the O language
+syntax and be ready to start writing your own programs. Let's dive in!
+
+@section An olang program
+
+An O programmin language program starts with a @code{main} function. This
+function must return the program exit code.
+
+@verbatim
+fn main(): u8 {
+  return 0;
+}
+@end verbatim
+
+To compile the program you can use @code{olc}.
+
+@verbatim
+$ olc my_prog.ol -o my_prog
+$ ./my_prog
+@end verbatim
+
+@section Functions
+
+Unlike C, O language does not require function prototypes. This means you can
+call a function before it's defined, making your code more flexible and easier
+to read in many cases.
+
+@verbatim
+fn main(): u8 {
+  return fib(8)
+}
+
+fn add(a: u32, b: u32): u32 {
+  return a + b
+}
+
+fn fib(n: u32): u32 {
+  if n <= 2 {
+    return n
+  }
+
+  return add(fib(n - 1), fib(n - 2))
+}
+@end verbatim
+
+@section Comments
+
+Comments starts with a @code{#}.
+
+@verbatim
+# Hi I'm a comment and I'll be ignored by the compiler.
+@end verbatim
+
+@section Variables
+
+@verbatim
+var answer: u32 = 42
+@end verbatim
+
+@section Flow control
+
+Any non zero expr is true.
+
+@verbatim
+if expr {
+  # statement
+} else {
+  # statement
+}
+@end verbatim
+
+@section Primitive data types
+
+@table @samp
+
+@item u8
+
+Unsigned 8 bits.
+
+@item u16
+
+Unsigned 16 bits.
+
+@item u32
+
+Unsigned 32 bits.
+
+@item u64
+
+Unsigned 64 bits.
+
+@end table
+
+@section Binary Operations
+
+Binary operations are pretty much like C.
+
+@subsection Logical
+
+@table @samp
+
+@item Equals
+
+@verbatim
+expr1 == expr2
+@end verbatim
+
+Results zero (false) or one (true).
+
+@item Less
+
+@verbatim
+expr1 < expr2
+@end verbatim
+
+Results zero (false) or one (true).
+
+@item Less Equal
+
+@verbatim
+expr1 <= expr2
+@end verbatim
+
+Results zero (false) or one (true).
+
+@item Greater
+
+@verbatim
+expr1 > expr2
+@end verbatim
+
+Results zero (false) or one (true).
+
+@item Greater Equal
+
+@verbatim
+expr1 >= expr2
+@end verbatim
+
+Results zero (false) or one (true).
+
+@item Or
+
+@verbatim
+expr1 || expr2
+@end verbatim
+
+Results zero (false) if both are true or one (true) if any is true.
+
+@item And
+
+@verbatim
+expr1 && expr2
+@end verbatim
+
+Results zero (false) if any is false or one (true) if both are true.
+
+@end table
+
+@subsection Bitwise
+
+@table @samp
+
+@item Shift left
+
+@verbatim
+n << bits
+@end verbatim
+
+@item Shift left
+
+@verbatim
+n >> bits
+@end verbatim
+
+@item And
+
+@verbatim
+n & bits
+@end verbatim
+
+@item Or
+
+@verbatim
+n | bits
+@end verbatim
+
+@end table
+
+@subsection Arithmetic
+
+@table @samp
+
+@item Addition
+
+@verbatim
+expr1 + expr2
+@end verbatim
+
+@item Subtraction
+
+@verbatim
+expr1 - expr2
+@end verbatim
+
+@item Multiplication
+
+@verbatim
+expr1 * expr2
+@end verbatim
+
+@item Division
+
+@verbatim
+expr1 / expr2
+@end verbatim
+
+@item Remaining
+
+@verbatim
+expr1 % expr2
+@end verbatim
+
+@end table
diff --git a/docs/info/introduction.texi b/docs/info/introduction.texi
index 4fb7cbc..579880f 100644
--- a/docs/info/introduction.texi
+++ b/docs/info/introduction.texi
@@ -1,3 +1,9 @@
 @node Introduction
 @chapter Introduction
 
+Welcome to the O programming manual. This manual will guide you to learn the
+language principles and how  to contribute if you are a low level geek.
+
+Olang is a project maintained by enthusiast individuals across the globe. Join
+us in the system programming language universe. Let's fight complexity
+together.

base-commit: 5415f1e48b199feb6d63461f0adfffafb13d005a
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang] docs: installation: add information about how to clone the project
@ 2024-10-07 16:29 Carlos Maniero
  2024-10-07 16:30 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-07 16:29 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
I was doing a setup of a new machine and I missed the git clone URL.
I know that we may change the URL in the future but I think that it is
easier for someone that are trying to install olc to have all
information at the same page.

 docs/info/installation.texi | 24 +++++++++++++++++++++---
 1 file changed, 21 insertions(+), 3 deletions(-)

diff --git a/docs/info/installation.texi b/docs/info/installation.texi
index 6ee4a4d..7d63964 100644
--- a/docs/info/installation.texi
+++ b/docs/info/installation.texi
@@ -1,10 +1,28 @@
 @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.
+This installation assumes you are running an unix-like operation system.
 
-The following commands will compile the the code and install @code{olc} binary
+@section Dependencies
+
+@itemize @bullet
+@item git
+@item make
+@item makeinfo
+@item c compiler (gcc, clang, etc)
+@end itemize
+
+@section Installing olc from source
+
+The olang is still in its early stages, so it's not packaged for major package
+managers. To install it, clone the source code and follow the installation
+instructions.
+
+@verbatim
+git clone https://git.sr.ht/~johnnyrichard/olang
+@end verbatim
+
+The following commands will compile the code and install @code{olc} binary
 (olang compiler), @code{man} pages and @code{info} docs into your system.
 
 @verbatim

base-commit: 2fcf01737ed421c3aad49712f09063f1693c11d4
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1] chore: parser: remove old unit test for parsing
@ 2024-10-06 13:44 Johnny Richard
  2024-10-06 11:45 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Johnny Richard @ 2024-10-06 13:44 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

We have introduced a new way of testing the ast output from the parser,
this test does not longer add value to the test suite.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 tests/unit/parser_test.c | 97 ----------------------------------------
 1 file changed, 97 deletions(-)
 delete mode 100644 tests/unit/parser_test.c

diff --git a/tests/unit/parser_test.c b/tests/unit/parser_test.c
deleted file mode 100644
index c834261..0000000
--- a/tests/unit/parser_test.c
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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_translation_unit_test(const MunitParameter params[], void *user_data_or_fixture)
-{
-    arena_t arena = arena_new(ARENA_CAPACITY);
-
-    char *filepath = "main.0";
-    char *source_value = "fn main(): u32 {\n\treturn 69\n}";
-
-    lexer_t lexer;
-    string_view_t code = { .chars = source_value, .size = strlen(source_value) };
-    lexer_init(&lexer, (source_code_t){ .code = code, .filepath = filepath });
-
-    parser_t parser;
-    parser_init(&parser, &lexer, &arena);
-
-    ast_node_t *translation_unit_node = parser_parse_translation_unit(&parser);
-    assert_not_null(translation_unit_node);
-    assert_uint(translation_unit_node->kind, ==, AST_NODE_TRANSLATION_UNIT);
-
-    ast_translation_unit_t translation_unit = translation_unit_node->as_translation_unit;
-
-    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");
-
-    ast_node_t *block = fn.block;
-    assert_not_null(block);
-
-    assert_uint(block->kind, ==, AST_NODE_BLOCK);
-    assert_uint(list_size(block->as_block.nodes), ==, 1);
-    list_item_t *block_item = list_get(block->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->as_return_stmt.expr;
-    assert_not_null(number_node);
-    assert_uint(number_node->kind, ==, AST_NODE_LITERAL);
-    assert_uint(number_node->as_literal.kind, ==, AST_LITERAL_U32);
-    assert_uint(number_node->as_literal.as_u32, ==, 69);
-
-    arena_free(&arena);
-
-    return MUNIT_OK;
-}
-
-static MunitTest tests[] = {
-    { "/parse_translation_unit", parse_translation_unit_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;
-}

base-commit: c4f7fa1e4ccd7c9d9e2d9f835feefdbd9c312c2c
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1 2/2] codegen: x64: evaluate expression considering the data return data size
@ 2024-10-05  6:59 Carlos Maniero
  2024-10-05  7:00 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-05  6:59 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

The expressions were always considering that it results in a 64 bits
operation. Now, it evaluates the result size and use the proper
register.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 src/codegen_linux_x86_64.c      | 314 ++++++++++++++++++++++----------
 tests/olc/0028_function_call.ol |   4 +-
 2 files changed, 220 insertions(+), 98 deletions(-)

diff --git a/src/codegen_linux_x86_64.c b/src/codegen_linux_x86_64.c
index e04de0e..e501634 100644
--- a/src/codegen_linux_x86_64.c
+++ b/src/codegen_linux_x86_64.c
@@ -31,6 +31,8 @@
 #define X86_CALL_EIP_STACK_OFFSET (8)
 #define X86_CALL_ARG_SIZE 6
 
+#define bytes_max(a, b) ((a) > (b) ? (a) : (b))
+
 typedef enum x86_64_register_type
 {
     REG_ACCUMULATOR,
@@ -132,7 +134,9 @@ codegen_linux_x86_64_get_next_label(codegen_x86_64_t *codegen)
     return ++codegen->label_index;
 }
 
-static void
+typedef size_t size_in_bytes_t;
+
+static size_in_bytes_t
 codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr_node)
 {
     switch (expr_node->kind) {
@@ -141,8 +145,8 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
             assert(literal_u32.kind == AST_LITERAL_U32);
             uint32_t n = literal_u32.as_u32;
 
-            fprintf(codegen->out, "    mov $%d, %%rax\n", n);
-            return;
+            fprintf(codegen->out, "    mov $%d, %%eax\n", n);
+            return 4;
         }
         case AST_NODE_REF: {
             ast_ref_t ref = expr_node->as_ref;
@@ -156,15 +160,17 @@ 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), %s\n",
-                    *offset,
-                    get_reg_for(REG_ACCUMULATOR, type_to_bytes(&symbol->type)));
-            return;
+            size_t bytes = type_to_bytes(&symbol->type);
+
+            fprintf(codegen->out, "    mov -%ld(%%rbp), %s\n", *offset, get_reg_for(REG_ACCUMULATOR, bytes));
+            return bytes;
         }
         case AST_NODE_FN_CALL: {
             ast_fn_call_t fn_call = expr_node->as_fn_call;
 
+            symbol_t *symbol = scope_lookup(fn_call.scope, fn_call.id);
+            assert(symbol);
+
             size_t i = 0;
             for (list_item_t *item = list_head(fn_call.args); item != NULL; item = list_next(item)) {
                 // FIXME: add support for more args than X86_CALL_ARG_SIZE
@@ -174,7 +180,6 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
 
                 codegen_linux_x86_64_emit_expression(codegen, arg_node);
 
-                // FIXME: should get the correct size according to the ast node
                 fprintf(codegen->out, "    push %s\n", get_reg_for(REG_ACCUMULATOR, 8));
                 ++i;
             }
@@ -184,223 +189,339 @@ codegen_linux_x86_64_emit_expression(codegen_x86_64_t *codegen, ast_node_t *expr
             }
 
             fprintf(codegen->out, "    call " SV_FMT "\n", SV_ARG(fn_call.id));
-            return;
+
+            return type_to_bytes(&symbol->type);
         }
         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(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+
+                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+
                     fprintf(codegen->out, "    pop %%rcx\n");
-                    fprintf(codegen->out, "    add %%rcx, %%rax\n");
+                    fprintf(codegen->out,
+                            "    add %s, %s\n",
+                            get_reg_for(REG_COUNTER, expr_bytes),
+                            get_reg_for(REG_ACCUMULATOR, expr_bytes));
 
-                    return;
+                    return expr_bytes;
                 }
                 case AST_BINOP_MULTIPLICATION: {
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+
+                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+
                     fprintf(codegen->out, "    pop %%rcx\n");
-                    fprintf(codegen->out, "    mul %%rcx\n");
+                    fprintf(codegen->out, "    mul %s\n", get_reg_for(REG_COUNTER, expr_bytes));
 
-                    return;
+                    return expr_bytes;
                 }
                 case AST_BINOP_DIVISION: {
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+
+                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+
                     fprintf(codegen->out, "    pop %%rcx\n");
                     fprintf(codegen->out, "    xor %%rdx, %%rdx\n");
-                    fprintf(codegen->out, "    div %%rcx\n");
+                    fprintf(codegen->out, "    div %s\n", get_reg_for(REG_COUNTER, expr_bytes));
 
-                    return;
+                    return expr_bytes;
                 }
                 case AST_BINOP_REMINDER: {
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t rhs_bytes = 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(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+
+                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+
                     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");
+                    fprintf(codegen->out, "    div %s\n", get_reg_for(REG_COUNTER, expr_bytes));
+                    fprintf(codegen->out,
+                            "    mov %s, %s\n",
+                            get_reg_for(REG_DATA, expr_bytes),
+                            get_reg_for(REG_ACCUMULATOR, expr_bytes));
 
-                    return;
+                    return expr_bytes;
                 }
                 case AST_BINOP_SUBTRACTION: {
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+
+                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+
                     fprintf(codegen->out, "    pop %%rcx\n");
-                    fprintf(codegen->out, "    sub %%rcx, %%rax\n");
+                    fprintf(codegen->out,
+                            "    sub %s, %s\n",
+                            get_reg_for(REG_COUNTER, expr_bytes),
+                            get_reg_for(REG_ACCUMULATOR, expr_bytes));
 
-                    return;
+                    return expr_bytes;
                 }
                 case AST_BINOP_CMP_EQ: {
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+
+                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+
                     fprintf(codegen->out, "    pop %%rcx\n");
-                    fprintf(codegen->out, "    cmp %%rcx, %%rax\n");
+                    fprintf(codegen->out,
+                            "    cmp %s, %s\n",
+                            get_reg_for(REG_COUNTER, expr_bytes),
+                            get_reg_for(REG_ACCUMULATOR, expr_bytes));
                     fprintf(codegen->out, "    sete %%al\n");
-                    fprintf(codegen->out, "    movzb %%al, %%rax\n");
+                    fprintf(codegen->out, "    movzb %%al, %s\n", get_reg_for(REG_ACCUMULATOR, expr_bytes));
 
-                    return;
+                    return expr_bytes;
                 }
                 case AST_BINOP_CMP_LT: {
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+
+                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+
                     fprintf(codegen->out, "    pop %%rcx\n");
-                    fprintf(codegen->out, "    cmp %%rcx, %%rax\n");
+                    fprintf(codegen->out,
+                            "    cmp %s, %s\n",
+                            get_reg_for(REG_COUNTER, expr_bytes),
+                            get_reg_for(REG_ACCUMULATOR, expr_bytes));
                     fprintf(codegen->out, "    setl %%al\n");
-                    fprintf(codegen->out, "    movzb %%al, %%rax\n");
+                    fprintf(codegen->out, "    movzb %%al, %s\n", get_reg_for(REG_ACCUMULATOR, expr_bytes));
 
-                    return;
+                    return expr_bytes;
                 }
                 case AST_BINOP_CMP_GT: {
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+
+                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+
                     fprintf(codegen->out, "    pop %%rcx\n");
-                    fprintf(codegen->out, "    cmp %%rcx, %%rax\n");
+                    fprintf(codegen->out,
+                            "    cmp %s, %s\n",
+                            get_reg_for(REG_COUNTER, expr_bytes),
+                            get_reg_for(REG_ACCUMULATOR, expr_bytes));
                     fprintf(codegen->out, "    setg %%al\n");
-                    fprintf(codegen->out, "    movzb %%al, %%rax\n");
+                    fprintf(codegen->out, "    movzb %%al, %s\n", get_reg_for(REG_ACCUMULATOR, expr_bytes));
 
-                    return;
+                    return expr_bytes;
                 }
                 case AST_BINOP_CMP_NEQ: {
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+
+                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+
                     fprintf(codegen->out, "    pop %%rcx\n");
-                    fprintf(codegen->out, "    cmp %%rcx, %%rax\n");
+                    fprintf(codegen->out,
+                            "    cmp %s, %s\n",
+                            get_reg_for(REG_COUNTER, expr_bytes),
+                            get_reg_for(REG_ACCUMULATOR, expr_bytes));
                     fprintf(codegen->out, "    setne %%al\n");
-                    fprintf(codegen->out, "    movzb %%al, %%rax\n");
+                    fprintf(codegen->out, "    movzb %%al, %s\n", get_reg_for(REG_ACCUMULATOR, expr_bytes));
 
-                    return;
+                    return expr_bytes;
                 }
                 case AST_BINOP_CMP_LEQ: {
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+
+                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+
                     fprintf(codegen->out, "    pop %%rcx\n");
-                    fprintf(codegen->out, "    cmp %%rcx, %%rax\n");
+                    fprintf(codegen->out,
+                            "    cmp %s, %s\n",
+                            get_reg_for(REG_COUNTER, expr_bytes),
+                            get_reg_for(REG_ACCUMULATOR, expr_bytes));
                     fprintf(codegen->out, "    setle %%al\n");
-                    fprintf(codegen->out, "    movzb %%al, %%rax\n");
+                    fprintf(codegen->out, "    movzb %%al, %s\n", get_reg_for(REG_ACCUMULATOR, expr_bytes));
 
-                    return;
+                    return expr_bytes;
                 }
                 case AST_BINOP_CMP_GEQ: {
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+
+                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+
                     fprintf(codegen->out, "    pop %%rcx\n");
-                    fprintf(codegen->out, "    cmp %%rcx, %%rax\n");
+                    fprintf(codegen->out,
+                            "    cmp %s, %s\n",
+                            get_reg_for(REG_COUNTER, expr_bytes),
+                            get_reg_for(REG_ACCUMULATOR, expr_bytes));
                     fprintf(codegen->out, "    setge %%al\n");
-                    fprintf(codegen->out, "    movzb %%al, %%rax\n");
+                    fprintf(codegen->out, "    movzb %%al, %s\n", get_reg_for(REG_ACCUMULATOR, expr_bytes));
 
-                    return;
+                    return expr_bytes;
                 }
                 case AST_BINOP_BITWISE_LSHIFT: {
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+
                     fprintf(codegen->out, "    pop %%rcx\n");
-                    fprintf(codegen->out, "    shl %%cl, %%rax\n");
+                    fprintf(codegen->out, "    shl %%cl, %s\n", get_reg_for(REG_ACCUMULATOR, lhs_bytes));
 
-                    return;
+                    return lhs_bytes;
                 }
                 case AST_BINOP_BITWISE_RSHIFT: {
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
                     codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+
                     fprintf(codegen->out, "    pop %%rcx\n");
-                    fprintf(codegen->out, "    shr %%cl, %%rax\n");
+                    fprintf(codegen->out, "    shr %%cl, %s\n", get_reg_for(REG_ACCUMULATOR, lhs_bytes));
 
-                    return;
+                    return lhs_bytes;
                 }
                 case AST_BINOP_BITWISE_XOR: {
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+
+                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+
                     fprintf(codegen->out, "    pop %%rcx\n");
-                    fprintf(codegen->out, "    xor %%ecx, %%eax\n");
+                    fprintf(codegen->out,
+                            "    xor %s, %s\n",
+                            get_reg_for(REG_COUNTER, expr_bytes),
+                            get_reg_for(REG_ACCUMULATOR, expr_bytes));
 
-                    return;
+                    return expr_bytes;
                 }
                 case AST_BINOP_BITWISE_AND: {
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+
+                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+
                     fprintf(codegen->out, "    pop %%rcx\n");
-                    fprintf(codegen->out, "    and %%ecx, %%eax\n");
+                    fprintf(codegen->out,
+                            "    and %s, %s\n",
+                            get_reg_for(REG_COUNTER, expr_bytes),
+                            get_reg_for(REG_ACCUMULATOR, expr_bytes));
 
-                    return;
+                    return expr_bytes;
                 }
                 case AST_BINOP_BITWISE_OR: {
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
                     fprintf(codegen->out, "    push %%rax\n");
 
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+
+                    size_in_bytes_t expr_bytes = bytes_max(rhs_bytes, lhs_bytes);
+
                     fprintf(codegen->out, "    pop %%rcx\n");
-                    fprintf(codegen->out, "    or %%ecx, %%eax\n");
+                    fprintf(codegen->out,
+                            "    or %s, %s\n",
+                            get_reg_for(REG_COUNTER, expr_bytes),
+                            get_reg_for(REG_ACCUMULATOR, expr_bytes));
 
-                    return;
+                    return expr_bytes;
                 }
                 case AST_BINOP_LOGICAL_AND: {
                     size_t label_exit = codegen_linux_x86_64_get_next_label(codegen);
 
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
-                    fprintf(codegen->out, "    cmp $0, %%rax\n");
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    cmp $0, %s\n", get_reg_for(REG_ACCUMULATOR, lhs_bytes));
                     fprintf(codegen->out, "    je .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, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    cmp $0, %s\n", get_reg_for(REG_ACCUMULATOR, rhs_bytes));
                     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;
+                    return 1;
                 }
                 case AST_BINOP_LOGICAL_OR: {
                     size_t label_t = codegen_linux_x86_64_get_next_label(codegen);
                     size_t label_f = codegen_linux_x86_64_get_next_label(codegen);
 
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
-                    fprintf(codegen->out, "    cmp $0, %%rax\n");
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t lhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.lhs);
+                    fprintf(codegen->out, "    cmp $0, %s\n", get_reg_for(REG_ACCUMULATOR, lhs_bytes));
                     fprintf(codegen->out, "    jne .L%ld\n", label_t);
 
-                    codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
-                    fprintf(codegen->out, "    cmp $0, %%rax\n");
+                    fprintf(codegen->out, "    xor %%rax, %%rax\n");
+                    size_in_bytes_t rhs_bytes = codegen_linux_x86_64_emit_expression(codegen, bin_op.rhs);
+                    fprintf(codegen->out, "    cmp $0, %s\n", get_reg_for(REG_ACCUMULATOR, rhs_bytes));
                     fprintf(codegen->out, "    je .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;
+                    return 1;
                 }
                 default: {
                     assert(0 && "unsupported binary operation");
-                    return;
+                    return 0;
                 }
             }
         }
@@ -521,7 +642,9 @@ calculate_fn_local_size(scope_t *scope)
 {
     assert(scope);
 
-    size_t local_size = 0;
+    // The local_size starts with 8 bytes since the first 8 bytes from the
+    // stack are reserved to store RBP during the prelude
+    size_t local_size = 8;
 
     map_kv_t *kvs[scope->symbols->size];
 
@@ -578,10 +701,9 @@ codegen_linux_x86_64_emit_function(codegen_x86_64_t *codegen, ast_fn_definition_
         map_put(codegen->symbols_stack_offset, symbol_ptr, offset);
 
         fprintf(codegen->out,
-                "    mov %s, -%ld(%s)\n",
-                get_reg_for(x86_call_args[i], 8),
-                *offset,
-                get_reg_for(REG_BASE_PTR, 8));
+                "    mov %s, -%ld(%%rbp)\n",
+                get_reg_for(x86_call_args[i], symbol->type.as_primitive.size),
+                *offset);
 
         // FIXME: add offset according to the param size
         codegen->base_offset += 8;
diff --git a/tests/olc/0028_function_call.ol b/tests/olc/0028_function_call.ol
index cfd06de..b32fbbf 100644
--- a/tests/olc/0028_function_call.ol
+++ b/tests/olc/0028_function_call.ol
@@ -17,7 +17,7 @@ fn main(): u8 {
   return add(40, 2) 
 }
 
-fn add(a: u64, b: u64): u8 {
+fn add(a: u32, b: u64): u8 {
   return a + b
 }
 
@@ -34,7 +34,7 @@ fn add(a: u64, b: u64): u8 {
 # |       |-Literal <kind:u32> <value:40>
 # |       `-Literal <kind:u32> <value:2>
 # `-Function_Definition <name:add> <return:u8>
-#   |-Param_Definition <name:a> <type:u64>
+#   |-Param_Definition <name:a> <type:u32>
 #   |-Param_Definition <name:b> <type:u64>
 #   `-Block
 #     `-Return_Statement
-- 
2.34.1


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang] lexer: create token location structure
@ 2024-10-04 22:12 Carlos Maniero
  2024-10-04 22:12 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-04 22:12 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

To reach the source line and column to print errors, we use to get the
filepath from parser, the cursor from token and the source code from the
lexer.

Now, instead of navigating into these three data structures, the same
information must be accessible from a single place, the token's location
*token_loc_t*.

The source_code.h file was removed once the source code struct was added
to the lexer.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 src/lexer.c              | 26 +++++++++++++++++++-------
 src/lexer.h              | 23 ++++++++++++++++++++---
 src/main.c               | 18 +++++++++---------
 src/parser.c             | 33 +++++++++++++++------------------
 src/parser.h             |  4 +---
 src/source_code.h        | 28 ----------------------------
 tests/unit/parser_test.c |  2 +-
 7 files changed, 65 insertions(+), 69 deletions(-)
 delete mode 100644 src/source_code.h

diff --git a/src/lexer.c b/src/lexer.c
index 523822f..4784f1c 100644
--- a/src/lexer.c
+++ b/src/lexer.c
@@ -396,21 +396,21 @@ static void
 lexer_init_char_value_token(lexer_t *lexer, token_t *token, token_kind_t kind)
 {
     string_view_t str = { .chars = lexer->src.code.chars + lexer->cur.offset, .size = 1 };
-    *token = (token_t){ .kind = kind, .value = str, .cur = lexer->cur };
+    *token = (token_t){ .kind = kind, .value = str, .loc = (token_loc_t){ .src = lexer->src, .cur = lexer->cur } };
 }
 
 static void
 lexer_init_str_value_token(lexer_t *lexer, token_t *token, token_kind_t kind, lexer_cursor_t cur)
 {
     string_view_t str = { .chars = lexer->src.code.chars + cur.offset, .size = lexer->cur.offset - cur.offset };
-    *token = (token_t){ .kind = kind, .value = str, .cur = cur };
+    *token = (token_t){ .kind = kind, .value = str, .loc = (token_loc_t){ .src = lexer->src, .cur = cur } };
 }
 
 static void
 lexer_init_eof_token(lexer_t *lexer, token_t *token)
 {
     string_view_t str = { 0 };
-    *token = (token_t){ .kind = TOKEN_EOF, .value = str, .cur = lexer->cur };
+    *token = (token_t){ .kind = TOKEN_EOF, .value = str, .loc = (token_loc_t){ .src = lexer->src, .cur = lexer->cur } };
 }
 
 static token_kind_t
@@ -458,14 +458,26 @@ lexer_lookahead(lexer_t *lexer, token_t *token, size_t n)
 }
 
 string_view_t
-lexer_get_token_line(lexer_t *lexer, token_t *token)
+token_loc_to_line(token_loc_t loc)
 {
-    size_t offset = token->cur.bol;
-    string_view_t line = { .chars = lexer->src.code.chars + offset, .size = 0 };
+    size_t offset = loc.cur.bol;
+    string_view_t line = { .chars = loc.src.code.chars + offset, .size = 0 };
 
-    while ((line.size + offset) < lexer->src.code.size && line.chars[line.size] != '\n' && line.chars[line.size] != 0) {
+    while ((line.size + offset) < loc.src.code.size && line.chars[line.size] != '\n' && line.chars[line.size] != 0) {
         ++line.size;
     }
 
     return line;
 }
+
+size_t
+token_loc_to_lineno(token_loc_t loc)
+{
+    return loc.cur.row + 1;
+}
+
+size_t
+token_loc_to_colno(token_loc_t loc)
+{
+    return loc.cur.offset - loc.cur.bol + 1;
+}
diff --git a/src/lexer.h b/src/lexer.h
index c5a342a..bb39a70 100644
--- a/src/lexer.h
+++ b/src/lexer.h
@@ -17,11 +17,16 @@
 #ifndef LEXER_H
 #define LEXER_H
 
-#include "source_code.h"
 #include "string_view.h"
 #include <stdint.h>
 #include <stdio.h>
 
+typedef struct source_code
+{
+    char *filepath;
+    string_view_t code;
+} source_code_t;
+
 typedef struct lexer_cursor
 {
     size_t offset;
@@ -85,13 +90,25 @@ typedef enum token_kind
     TOKEN_EOF
 } token_kind_t;
 
+typedef struct token_loc
+{
+    source_code_t src;
+    lexer_cursor_t cur;
+} token_loc_t;
+
 typedef struct token
 {
     token_kind_t kind;
     string_view_t value;
-    lexer_cursor_t cur;
+    token_loc_t loc;
 } token_t;
 
+size_t
+token_loc_to_lineno(token_loc_t loc);
+
+size_t
+token_loc_to_colno(token_loc_t loc);
+
 void
 lexer_init(lexer_t *lexer, source_code_t src);
 
@@ -111,6 +128,6 @@ bool
 token_kind_is_binary_op(token_kind_t kind);
 
 string_view_t
-lexer_get_token_line(lexer_t *lexer, token_t *token);
+token_loc_to_line(token_loc_t loc);
 
 #endif /* LEXER_H */
diff --git a/src/main.c b/src/main.c
index 4c8f2a5..d1c76e3 100644
--- a/src/main.c
+++ b/src/main.c
@@ -44,7 +44,7 @@ void
 handle_codegen_linux(cli_opts_t *opts);
 
 static void
-print_token(char *filepath, token_t *token);
+print_token(token_t *token);
 
 source_code_t
 read_entire_file(char *filepath, arena_t *arena);
@@ -89,10 +89,10 @@ handle_dump_tokens(cli_opts_t *opts)
     token_t token = { 0 };
     lexer_next_token(&lexer, &token);
     while (token.kind != TOKEN_EOF) {
-        print_token(opts->filepath, &token);
+        print_token(&token);
         lexer_next_token(&lexer, &token);
     }
-    print_token(opts->filepath, &token);
+    print_token(&token);
 
     arena_free(&arena);
 }
@@ -112,7 +112,7 @@ handle_dump_ast(cli_opts_t *opts)
     source_code_t src = read_entire_file(opts->filepath, &arena);
 
     lexer_init(&lexer, src);
-    parser_init(&parser, &lexer, &arena, opts->filepath);
+    parser_init(&parser, &lexer, &arena);
 
     ast_node_t *ast = parser_parse_translation_unit(&parser);
 
@@ -133,7 +133,7 @@ handle_codegen_linux(cli_opts_t *opts)
 
     source_code_t src = read_entire_file(opts->filepath, &arena);
     lexer_init(&lexer, src);
-    parser_init(&parser, &lexer, &arena, opts->filepath);
+    parser_init(&parser, &lexer, &arena);
 
     ast_node_t *ast = parser_parse_translation_unit(&parser);
 
@@ -242,11 +242,11 @@ read_entire_file(char *filepath, arena_t *arena)
 }
 
 static void
-print_token(char *filepath, token_t *token)
+print_token(token_t *token)
 {
     printf("%s:%lu:%lu: <%s>\n",
-           filepath,
-           token->cur.row + 1,
-           (token->cur.offset - token->cur.bol) + 1,
+           token->loc.src.filepath,
+           token_loc_to_lineno(token->loc),
+           token_loc_to_colno(token->loc),
            token_kind_to_cstr(token->kind));
 }
diff --git a/src/parser.c b/src/parser.c
index 26e5465..ecc10f0 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -32,7 +32,7 @@ static bool
 expected_next_token(parser_t *parser, token_t *token, token_kind_t kind);
 
 static bool
-expected_token(parser_t *parser, token_t *token, token_kind_t kind);
+expected_token(token_t *token, token_kind_t kind);
 
 static bool
 parser_parse_type(parser_t *parser, string_view_t *type);
@@ -68,14 +68,12 @@ static void
 skip_line_feeds(lexer_t *lexer);
 
 void
-parser_init(parser_t *parser, lexer_t *lexer, arena_t *arena, char *file_path)
+parser_init(parser_t *parser, lexer_t *lexer, arena_t *arena)
 {
     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 *
@@ -311,7 +309,7 @@ parser_parse_fn_args(parser_t *parser)
     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)) {
+        if (is_not_first_arg && expected_token(&token, TOKEN_COMMA)) {
             lexer_next_token(parser->lexer, &token);
         }
 
@@ -353,11 +351,11 @@ parser_parse_fn_params(parser_t *parser)
     bool is_not_first_param = false;
 
     while (token.kind != TOKEN_CPAREN && token.kind != TOKEN_EOF) {
-        if (is_not_first_param && expected_token(parser, &token, TOKEN_COMMA)) {
+        if (is_not_first_param && expected_token(&token, TOKEN_COMMA)) {
             lexer_next_token(parser->lexer, &token);
         }
 
-        if (!expected_token(parser, &token, TOKEN_ID)) {
+        if (!expected_token(&token, TOKEN_ID)) {
             return NULL;
         }
 
@@ -372,7 +370,7 @@ parser_parse_fn_params(parser_t *parser)
         is_not_first_param = true;
     }
 
-    if (!expected_token(parser, &token, TOKEN_CPAREN)) {
+    if (!expected_token(&token, TOKEN_CPAREN)) {
         return NULL;
     }
 
@@ -555,7 +553,7 @@ parser_parse_if_stmt(parser_t *parser)
             return NULL;
         }
 
-    } else if (!expected_token(parser, &next_token, TOKEN_LF)) {
+    } else if (!expected_token(&next_token, TOKEN_LF)) {
         return NULL;
     }
 
@@ -613,24 +611,23 @@ static bool
 expected_next_token(parser_t *parser, token_t *token, token_kind_t expected_kind)
 {
     lexer_next_token(parser->lexer, token);
-    return expected_token(parser, token, expected_kind);
+    return expected_token(token, expected_kind);
 }
 
 static bool
-expected_token(parser_t *parser, token_t *token, token_kind_t expected_kind)
+expected_token(token_t *token, token_kind_t expected_kind)
 {
     if (token->kind != expected_kind) {
         fprintf(stderr,
-                "%s:%lu:%lu: error: got '" SV_FMT "' token but expect <%s>\n",
-                parser->file_path,
-                token->cur.row + 1,
-                (token->cur.offset - token->cur.bol) + 1,
+                "%s:%lu:%lu: syntax error: got '" SV_FMT "' token but expect '%s'\n",
+                token->loc.src.filepath,
+                token_loc_to_lineno(token->loc),
+                token_loc_to_colno(token->loc),
                 SV_ARG(token->value),
                 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->cur.offset - token->cur.bol + 1), "^");
+        fprintf(stderr, SV_FMT "\n", SV_ARG(token_loc_to_line(token->loc)));
+        fprintf(stderr, "%*s\n", (int)token_loc_to_colno(token->loc), "^");
 
         exit(EXIT_FAILURE);
     }
diff --git a/src/parser.h b/src/parser.h
index 31c0dc3..7db2b74 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -25,12 +25,10 @@ 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);
+parser_init(parser_t *parser, lexer_t *lexer, arena_t *arena);
 
 ast_node_t *
 parser_parse_translation_unit(parser_t *parser);
diff --git a/src/source_code.h b/src/source_code.h
deleted file mode 100644
index 2c774c7..0000000
--- a/src/source_code.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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 "string_view.h"
-
-#ifndef SOURCE_CODE_H
-#define SOURCE_CODE_H
-
-typedef struct source_code
-{
-    char *filepath;
-    string_view_t code;
-} source_code_t;
-
-#endif
diff --git a/tests/unit/parser_test.c b/tests/unit/parser_test.c
index 9eb56fd..c834261 100644
--- a/tests/unit/parser_test.c
+++ b/tests/unit/parser_test.c
@@ -39,7 +39,7 @@ parse_translation_unit_test(const MunitParameter params[], void *user_data_or_fi
     lexer_init(&lexer, (source_code_t){ .code = code, .filepath = filepath });
 
     parser_t parser;
-    parser_init(&parser, &lexer, &arena, filepath);
+    parser_init(&parser, &lexer, &arena);
 
     ast_node_t *translation_unit_node = parser_parse_translation_unit(&parser);
     assert_not_null(translation_unit_node);

base-commit: 83fd6730450617e85376acdec1f4bb396fff26c8
-- 
2.34.1


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang v1] lexer: add lexer cursor abstraction
@ 2024-10-04 17:51 Johnny Richard
  2024-10-04 15:52 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Johnny Richard @ 2024-10-04 17:51 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

In order to simplify the navigation and lexer state we are using a
common structure between tokens and lexer.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/lexer.c  | 96 ++++++++++++++++++++++++----------------------------
 src/lexer.h  | 18 +++++-----
 src/main.c   |  4 +--
 src/parser.c |  6 ++--
 4 files changed, 58 insertions(+), 66 deletions(-)

diff --git a/src/lexer.c b/src/lexer.c
index 6fe0151..8de40a0 100644
--- a/src/lexer.c
+++ b/src/lexer.c
@@ -26,9 +26,9 @@ lexer_init(lexer_t *lexer, string_view_t source)
 {
     assert(lexer);
     lexer->source = source;
-    lexer->offset = 0;
-    lexer->row = 0;
-    lexer->bol = 0;
+    lexer->cur.offset = 0;
+    lexer->cur.row = 0;
+    lexer->cur.bol = 0;
 }
 
 static char
@@ -50,7 +50,7 @@ static void
 lexer_init_char_value_token(lexer_t *lexer, token_t *token, token_kind_t kind);
 
 static void
-lexer_init_str_value_token(lexer_t *lexer, token_t *token, token_kind_t kind, size_t start_offset);
+lexer_init_str_value_token(lexer_t *lexer, token_t *token, token_kind_t kind, lexer_cursor_t cur);
 
 static void
 lexer_init_eof_token(lexer_t *lexer, token_t *token);
@@ -84,120 +84,121 @@ lexer_next_token(lexer_t *lexer, token_t *token)
         }
 
         if (isalpha(current_char)) {
-            size_t start_offset = lexer->offset;
+            lexer_cursor_t start_cur = lexer->cur;
             while (isalnum(current_char) && lexer_is_not_eof(lexer)) {
                 lexer_skip_char(lexer);
                 current_char = lexer_current_char(lexer);
             }
 
-            string_view_t text = { .chars = lexer->source.chars + start_offset, .size = lexer->offset - start_offset };
+            string_view_t text = { .chars = lexer->source.chars + start_cur.offset,
+                                   .size = lexer->cur.offset - start_cur.offset };
 
-            lexer_init_str_value_token(lexer, token, lexer_str_to_token_kind(text), start_offset);
+            lexer_init_str_value_token(lexer, token, lexer_str_to_token_kind(text), start_cur);
             return;
         }
 
         if (isdigit(current_char)) {
-            size_t start_offset = lexer->offset;
+            lexer_cursor_t start_cur = lexer->cur;
             while (isdigit(current_char) && lexer_is_not_eof(lexer)) {
                 lexer_skip_char(lexer);
                 current_char = lexer_current_char(lexer);
             }
 
-            lexer_init_str_value_token(lexer, token, TOKEN_NUMBER, start_offset);
+            lexer_init_str_value_token(lexer, token, TOKEN_NUMBER, start_cur);
             return;
         }
 
         switch (current_char) {
             case '=': {
-                size_t start_offset = lexer->offset;
+                lexer_cursor_t start_cur = lexer->cur;
                 lexer_skip_char(lexer);
 
                 if (lexer_current_char(lexer) == '=') {
                     lexer_skip_char(lexer);
-                    lexer_init_str_value_token(lexer, token, TOKEN_CMP_EQ, start_offset);
+                    lexer_init_str_value_token(lexer, token, TOKEN_CMP_EQ, start_cur);
                     return;
                 }
 
-                lexer_init_str_value_token(lexer, token, TOKEN_EQ, start_offset);
+                lexer_init_str_value_token(lexer, token, TOKEN_EQ, start_cur);
                 return;
             }
             case '!': {
-                size_t start_offset = lexer->offset;
+                lexer_cursor_t start_cur = lexer->cur;
                 lexer_skip_char(lexer);
 
                 if (lexer_current_char(lexer) == '=') {
                     lexer_skip_char(lexer);
-                    lexer_init_str_value_token(lexer, token, TOKEN_CMP_NEQ, start_offset);
+                    lexer_init_str_value_token(lexer, token, TOKEN_CMP_NEQ, start_cur);
                     return;
                 }
 
-                lexer_init_str_value_token(lexer, token, TOKEN_BANG, start_offset);
+                lexer_init_str_value_token(lexer, token, TOKEN_BANG, start_cur);
                 return;
             }
             case '&': {
-                size_t start_offset = lexer->offset;
+                lexer_cursor_t start_cur = lexer->cur;
                 lexer_skip_char(lexer);
 
                 if (lexer_current_char(lexer) == '&') {
                     lexer_skip_char(lexer);
-                    lexer_init_str_value_token(lexer, token, TOKEN_LOGICAL_AND, start_offset);
+                    lexer_init_str_value_token(lexer, token, TOKEN_LOGICAL_AND, start_cur);
                     return;
                 }
 
-                lexer_init_str_value_token(lexer, token, TOKEN_AND, start_offset);
+                lexer_init_str_value_token(lexer, token, TOKEN_AND, start_cur);
                 return;
             }
             case '|': {
-                size_t start_offset = lexer->offset;
+                lexer_cursor_t start_cur = lexer->cur;
                 lexer_skip_char(lexer);
 
                 if (lexer_current_char(lexer) == '|') {
                     lexer_skip_char(lexer);
-                    lexer_init_str_value_token(lexer, token, TOKEN_LOGICAL_OR, start_offset);
+                    lexer_init_str_value_token(lexer, token, TOKEN_LOGICAL_OR, start_cur);
                     return;
                 }
 
-                lexer_init_str_value_token(lexer, token, TOKEN_PIPE, start_offset);
+                lexer_init_str_value_token(lexer, token, TOKEN_PIPE, start_cur);
                 return;
             }
             case '<': {
-                size_t start_offset = lexer->offset;
+                lexer_cursor_t start_cur = lexer->cur;
                 lexer_skip_char(lexer);
 
                 switch (lexer_current_char(lexer)) {
                     case '<': {
                         lexer_skip_char(lexer);
-                        lexer_init_str_value_token(lexer, token, TOKEN_BITWISE_LSHIFT, start_offset);
+                        lexer_init_str_value_token(lexer, token, TOKEN_BITWISE_LSHIFT, start_cur);
                         return;
                     }
                     case '=': {
                         lexer_skip_char(lexer);
-                        lexer_init_str_value_token(lexer, token, TOKEN_CMP_LEQ, start_offset);
+                        lexer_init_str_value_token(lexer, token, TOKEN_CMP_LEQ, start_cur);
                         return;
                     }
                     default: {
-                        lexer_init_str_value_token(lexer, token, TOKEN_LT, start_offset);
+                        lexer_init_str_value_token(lexer, token, TOKEN_LT, start_cur);
                         return;
                     }
                 }
             }
             case '>': {
-                size_t start_offset = lexer->offset;
+                lexer_cursor_t start_cur = lexer->cur;
                 lexer_skip_char(lexer);
 
                 switch (lexer_current_char(lexer)) {
                     case '>': {
                         lexer_skip_char(lexer);
-                        lexer_init_str_value_token(lexer, token, TOKEN_BITWISE_RSHIFT, start_offset);
+                        lexer_init_str_value_token(lexer, token, TOKEN_BITWISE_RSHIFT, start_cur);
                         return;
                     }
                     case '=': {
                         lexer_skip_char(lexer);
-                        lexer_init_str_value_token(lexer, token, TOKEN_CMP_GEQ, start_offset);
+                        lexer_init_str_value_token(lexer, token, TOKEN_CMP_GEQ, start_cur);
                         return;
                     }
                     default: {
-                        lexer_init_str_value_token(lexer, token, TOKEN_GT, start_offset);
+                        lexer_init_str_value_token(lexer, token, TOKEN_GT, start_cur);
                         return;
                     }
                 }
@@ -358,25 +359,25 @@ token_kind_is_binary_op(token_kind_t kind)
 static char
 lexer_current_char(lexer_t *lexer)
 {
-    return lexer->source.chars[lexer->offset];
+    return lexer->source.chars[lexer->cur.offset];
 }
 
 static void
 lexer_skip_char(lexer_t *lexer)
 {
-    assert(lexer->offset < lexer->source.size);
+    assert(lexer->cur.offset < lexer->source.size);
     if (lexer_current_char(lexer) == '\n') {
-        lexer->row++;
-        lexer->bol = ++lexer->offset;
+        lexer->cur.row++;
+        lexer->cur.bol = ++lexer->cur.offset;
     } else {
-        lexer->offset++;
+        lexer->cur.offset++;
     }
 }
 
 static bool
 lexer_is_eof(lexer_t *lexer)
 {
-    return lexer->offset >= lexer->source.size;
+    return lexer->cur.offset >= lexer->source.size;
 }
 
 static bool
@@ -394,25 +395,22 @@ _isspace(char c)
 static void
 lexer_init_char_value_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 };
+    string_view_t str = { .chars = lexer->source.chars + lexer->cur.offset, .size = 1 };
+    *token = (token_t){ .kind = kind, .value = str, .cur = lexer->cur };
 }
 
 static void
-lexer_init_str_value_token(lexer_t *lexer, token_t *token, token_kind_t kind, size_t start_offset)
+lexer_init_str_value_token(lexer_t *lexer, token_t *token, token_kind_t kind, lexer_cursor_t cur)
 {
-    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 };
+    string_view_t str = { .chars = lexer->source.chars + cur.offset, .size = lexer->cur.offset - cur.offset };
+    *token = (token_t){ .kind = kind, .value = str, .cur = cur };
 }
 
 static void
 lexer_init_eof_token(lexer_t *lexer, token_t *token)
 {
     string_view_t str = { 0 };
-    token_loc_t location = { .offset = lexer->offset, .row = lexer->row, .bol = lexer->bol };
-    *token = (token_t){ .kind = TOKEN_EOF, .value = str, .location = location };
+    *token = (token_t){ .kind = TOKEN_EOF, .value = str, .cur = lexer->cur };
 }
 
 static token_kind_t
@@ -450,23 +448,19 @@ lexer_peek_next(lexer_t *lexer, token_t *token)
 void
 lexer_lookahead(lexer_t *lexer, token_t *token, size_t n)
 {
-    size_t previous_offset = lexer->offset;
-    size_t previous_row = lexer->row;
-    size_t previous_bol = lexer->bol;
+    lexer_cursor_t previous_cur = lexer->cur;
 
     for (size_t i = 0; i < n; ++i) {
         lexer_next_token(lexer, token);
     }
 
-    lexer->offset = previous_offset;
-    lexer->row = previous_row;
-    lexer->bol = previous_bol;
+    lexer->cur = previous_cur;
 }
 
 string_view_t
 lexer_get_token_line(lexer_t *lexer, token_t *token)
 {
-    size_t offset = token->location.bol;
+    size_t offset = token->cur.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) {
diff --git a/src/lexer.h b/src/lexer.h
index 2746e3e..1aecb11 100644
--- a/src/lexer.h
+++ b/src/lexer.h
@@ -21,12 +21,17 @@
 #include <stdint.h>
 #include <stdio.h>
 
-typedef struct lexer
+typedef struct lexer_cursor
 {
-    string_view_t source;
     size_t offset;
     size_t row;
     size_t bol;
+} lexer_cursor_t;
+
+typedef struct lexer
+{
+    string_view_t source;
+    lexer_cursor_t cur;
 } lexer_t;
 
 typedef enum token_kind
@@ -79,18 +84,11 @@ typedef enum token_kind
     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;
+    lexer_cursor_t cur;
 } token_t;
 
 void
diff --git a/src/main.c b/src/main.c
index 60b17bf..9d66455 100644
--- a/src/main.c
+++ b/src/main.c
@@ -246,7 +246,7 @@ print_token(char *file_path, token_t *token)
 {
     printf("%s:%lu:%lu: <%s>\n",
            file_path,
-           token->location.row + 1,
-           (token->location.offset - token->location.bol) + 1,
+           token->cur.row + 1,
+           (token->cur.offset - token->cur.bol) + 1,
            token_kind_to_cstr(token->kind));
 }
diff --git a/src/parser.c b/src/parser.c
index a025ed4..26e5465 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -623,14 +623,14 @@ expected_token(parser_t *parser, token_t *token, token_kind_t expected_kind)
         fprintf(stderr,
                 "%s:%lu:%lu: error: got '" SV_FMT "' token but expect <%s>\n",
                 parser->file_path,
-                token->location.row + 1,
-                (token->location.offset - token->location.bol) + 1,
+                token->cur.row + 1,
+                (token->cur.offset - token->cur.bol) + 1,
                 SV_ARG(token->value),
                 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), "^");
+        fprintf(stderr, "%*s\n", (int)(token->cur.offset - token->cur.bol + 1), "^");
 
         exit(EXIT_FAILURE);
     }

base-commit: 9a9b1e51387cc60eb2a388713431f659cf4703c9
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ messages in thread
* [PATCH olang] lexer: add source code abstraction
@ 2024-10-04 16:34 Carlos Maniero
  2024-10-04 16:34 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Carlos Maniero @ 2024-10-04 16:34 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

This struct stores the source code string and its filepath. This will be
used in the near future to create token's locations.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 src/cli.c                |  2 +-
 src/cli.h                |  2 +-
 src/lexer.c              | 20 ++++++-------
 src/lexer.h              |  5 ++--
 src/main.c               | 64 ++++++++++++++++++++--------------------
 src/source_code.h        | 28 ++++++++++++++++++
 tests/unit/parser_test.c |  8 ++---
 7 files changed, 79 insertions(+), 50 deletions(-)
 create mode 100644 src/source_code.h

diff --git a/src/cli.c b/src/cli.c
index fa73b60..9d0f875 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -57,7 +57,7 @@ cli_parse_args(int argc, char **argv)
             opts.options |= CLI_OPT_SYSROOT;
             cli_opts_parse_sysroot(&opts, &args);
         } else {
-            opts.file_path = arg;
+            opts.filepath = arg;
         }
         arg = cli_args_shift(&args);
     }
diff --git a/src/cli.h b/src/cli.h
index 3f4c3a9..1a93443 100644
--- a/src/cli.h
+++ b/src/cli.h
@@ -32,7 +32,7 @@ typedef struct cli_opts
     char *arch;
     char *sysroot;
     char *compiler_path;
-    char *file_path;
+    char *filepath;
     string_view_t output_bin;
 } cli_opts_t;
 
diff --git a/src/lexer.c b/src/lexer.c
index 8de40a0..523822f 100644
--- a/src/lexer.c
+++ b/src/lexer.c
@@ -22,10 +22,10 @@
 #include <stdio.h>
 
 void
-lexer_init(lexer_t *lexer, string_view_t source)
+lexer_init(lexer_t *lexer, source_code_t src)
 {
     assert(lexer);
-    lexer->source = source;
+    lexer->src = src;
     lexer->cur.offset = 0;
     lexer->cur.row = 0;
     lexer->cur.bol = 0;
@@ -90,7 +90,7 @@ lexer_next_token(lexer_t *lexer, token_t *token)
                 current_char = lexer_current_char(lexer);
             }
 
-            string_view_t text = { .chars = lexer->source.chars + start_cur.offset,
+            string_view_t text = { .chars = lexer->src.code.chars + start_cur.offset,
                                    .size = lexer->cur.offset - start_cur.offset };
 
             lexer_init_str_value_token(lexer, token, lexer_str_to_token_kind(text), start_cur);
@@ -359,13 +359,13 @@ token_kind_is_binary_op(token_kind_t kind)
 static char
 lexer_current_char(lexer_t *lexer)
 {
-    return lexer->source.chars[lexer->cur.offset];
+    return lexer->src.code.chars[lexer->cur.offset];
 }
 
 static void
 lexer_skip_char(lexer_t *lexer)
 {
-    assert(lexer->cur.offset < lexer->source.size);
+    assert(lexer->cur.offset < lexer->src.code.size);
     if (lexer_current_char(lexer) == '\n') {
         lexer->cur.row++;
         lexer->cur.bol = ++lexer->cur.offset;
@@ -377,7 +377,7 @@ lexer_skip_char(lexer_t *lexer)
 static bool
 lexer_is_eof(lexer_t *lexer)
 {
-    return lexer->cur.offset >= lexer->source.size;
+    return lexer->cur.offset >= lexer->src.code.size;
 }
 
 static bool
@@ -395,14 +395,14 @@ _isspace(char c)
 static void
 lexer_init_char_value_token(lexer_t *lexer, token_t *token, token_kind_t kind)
 {
-    string_view_t str = { .chars = lexer->source.chars + lexer->cur.offset, .size = 1 };
+    string_view_t str = { .chars = lexer->src.code.chars + lexer->cur.offset, .size = 1 };
     *token = (token_t){ .kind = kind, .value = str, .cur = lexer->cur };
 }
 
 static void
 lexer_init_str_value_token(lexer_t *lexer, token_t *token, token_kind_t kind, lexer_cursor_t cur)
 {
-    string_view_t str = { .chars = lexer->source.chars + cur.offset, .size = lexer->cur.offset - cur.offset };
+    string_view_t str = { .chars = lexer->src.code.chars + cur.offset, .size = lexer->cur.offset - cur.offset };
     *token = (token_t){ .kind = kind, .value = str, .cur = cur };
 }
 
@@ -461,9 +461,9 @@ string_view_t
 lexer_get_token_line(lexer_t *lexer, token_t *token)
 {
     size_t offset = token->cur.bol;
-    string_view_t line = { .chars = lexer->source.chars + offset, .size = 0 };
+    string_view_t line = { .chars = lexer->src.code.chars + offset, .size = 0 };
 
-    while ((line.size + offset) < lexer->source.size && line.chars[line.size] != '\n' && line.chars[line.size] != 0) {
+    while ((line.size + offset) < lexer->src.code.size && line.chars[line.size] != '\n' && line.chars[line.size] != 0) {
         ++line.size;
     }
 
diff --git a/src/lexer.h b/src/lexer.h
index 1aecb11..c5a342a 100644
--- a/src/lexer.h
+++ b/src/lexer.h
@@ -17,6 +17,7 @@
 #ifndef LEXER_H
 #define LEXER_H
 
+#include "source_code.h"
 #include "string_view.h"
 #include <stdint.h>
 #include <stdio.h>
@@ -30,7 +31,7 @@ typedef struct lexer_cursor
 
 typedef struct lexer
 {
-    string_view_t source;
+    source_code_t src;
     lexer_cursor_t cur;
 } lexer_t;
 
@@ -92,7 +93,7 @@ typedef struct token
 } token_t;
 
 void
-lexer_init(lexer_t *lexer, string_view_t source);
+lexer_init(lexer_t *lexer, source_code_t src);
 
 void
 lexer_next_token(lexer_t *lexer, token_t *token);
diff --git a/src/main.c b/src/main.c
index 9d66455..4c8f2a5 100644
--- a/src/main.c
+++ b/src/main.c
@@ -44,10 +44,10 @@ void
 handle_codegen_linux(cli_opts_t *opts);
 
 static void
-print_token(char *file_path, token_t *token);
+print_token(char *filepath, token_t *token);
 
-string_view_t
-read_entire_file(char *file_path, arena_t *arena);
+source_code_t
+read_entire_file(char *filepath, arena_t *arena);
 
 int
 main(int argc, char **argv)
@@ -75,24 +75,24 @@ main(int argc, char **argv)
 void
 handle_dump_tokens(cli_opts_t *opts)
 {
-    if (opts->file_path == NULL) {
+    if (opts->filepath == NULL) {
         cli_print_usage(stderr, opts->compiler_path);
         exit(EXIT_FAILURE);
     }
 
     arena_t arena = arena_new(ARENA_CAPACITY);
-    string_view_t file_content = read_entire_file(opts->file_path, &arena);
+    source_code_t src = read_entire_file(opts->filepath, &arena);
 
     lexer_t lexer = { 0 };
-    lexer_init(&lexer, file_content);
+    lexer_init(&lexer, src);
 
     token_t token = { 0 };
     lexer_next_token(&lexer, &token);
     while (token.kind != TOKEN_EOF) {
-        print_token(opts->file_path, &token);
+        print_token(opts->filepath, &token);
         lexer_next_token(&lexer, &token);
     }
-    print_token(opts->file_path, &token);
+    print_token(opts->filepath, &token);
 
     arena_free(&arena);
 }
@@ -100,7 +100,7 @@ handle_dump_tokens(cli_opts_t *opts)
 void
 handle_dump_ast(cli_opts_t *opts)
 {
-    if (opts->file_path == NULL) {
+    if (opts->filepath == NULL) {
         cli_print_usage(stderr, opts->compiler_path);
         exit(EXIT_FAILURE);
     }
@@ -109,10 +109,10 @@ handle_dump_ast(cli_opts_t *opts)
     lexer_t lexer = { 0 };
     parser_t parser = { 0 };
 
-    string_view_t file_content = read_entire_file(opts->file_path, &arena);
+    source_code_t src = read_entire_file(opts->filepath, &arena);
 
-    lexer_init(&lexer, file_content);
-    parser_init(&parser, &lexer, &arena, opts->file_path);
+    lexer_init(&lexer, src);
+    parser_init(&parser, &lexer, &arena, opts->filepath);
 
     ast_node_t *ast = parser_parse_translation_unit(&parser);
 
@@ -122,7 +122,7 @@ handle_dump_ast(cli_opts_t *opts)
 void
 handle_codegen_linux(cli_opts_t *opts)
 {
-    if (opts->file_path == NULL) {
+    if (opts->filepath == NULL) {
         cli_print_usage(stderr, opts->compiler_path);
         exit(EXIT_FAILURE);
     }
@@ -131,9 +131,9 @@ handle_codegen_linux(cli_opts_t *opts)
     lexer_t lexer = { 0 };
     parser_t parser = { 0 };
 
-    string_view_t file_content = read_entire_file(opts->file_path, &arena);
-    lexer_init(&lexer, file_content);
-    parser_init(&parser, &lexer, &arena, opts->file_path);
+    source_code_t src = read_entire_file(opts->filepath, &arena);
+    lexer_init(&lexer, src);
+    parser_init(&parser, &lexer, &arena, opts->filepath);
 
     ast_node_t *ast = parser_parse_translation_unit(&parser);
 
@@ -204,48 +204,48 @@ handle_codegen_linux(cli_opts_t *opts)
     arena_free(&arena);
 }
 
-string_view_t
-read_entire_file(char *file_path, arena_t *arena)
+source_code_t
+read_entire_file(char *filepath, arena_t *arena)
 {
-    FILE *stream = fopen(file_path, "rb");
+    FILE *stream = fopen(filepath, "rb");
 
     if (stream == NULL) {
-        fprintf(stderr, "error: could not open file %s: %s\n", file_path, strerror(errno));
+        fprintf(stderr, "error: could not open file %s: %s\n", filepath, strerror(errno));
         exit(EXIT_FAILURE);
     }
 
-    string_view_t file_content = { 0 };
+    string_view_t code = { 0 };
 
     fseek(stream, 0, SEEK_END);
-    file_content.size = ftell(stream);
+    code.size = ftell(stream);
     fseek(stream, 0, SEEK_SET);
 
-    assert(file_content.size * 2 < ARENA_CAPACITY);
+    assert(code.size * 2 < ARENA_CAPACITY);
 
-    file_content.chars = (char *)arena_alloc(arena, (size_t)file_content.size);
+    code.chars = (char *)arena_alloc(arena, (size_t)code.size);
 
-    if (file_content.chars == NULL) {
-        fprintf(stderr, "error: could not read file %s: %s\n", file_path, strerror(errno));
+    if (code.chars == NULL) {
+        fprintf(stderr, "error: could not read file %s: %s\n", filepath, strerror(errno));
         exit(EXIT_FAILURE);
     }
 
-    size_t read_bytes = fread(file_content.chars, 1, file_content.size, stream);
+    size_t read_bytes = fread(code.chars, 1, code.size, stream);
 
-    if (read_bytes != file_content.size) {
-        fprintf(stderr, "error: failed to read all file bytes %s\n", file_path);
+    if (read_bytes != code.size) {
+        fprintf(stderr, "error: failed to read all file bytes %s\n", filepath);
         exit(EXIT_FAILURE);
     }
 
     fclose(stream);
 
-    return file_content;
+    return (source_code_t){ .filepath = filepath, .code = code };
 }
 
 static void
-print_token(char *file_path, token_t *token)
+print_token(char *filepath, token_t *token)
 {
     printf("%s:%lu:%lu: <%s>\n",
-           file_path,
+           filepath,
            token->cur.row + 1,
            (token->cur.offset - token->cur.bol) + 1,
            token_kind_to_cstr(token->kind));
diff --git a/src/source_code.h b/src/source_code.h
new file mode 100644
index 0000000..2c774c7
--- /dev/null
+++ b/src/source_code.h
@@ -0,0 +1,28 @@
+/*
+ * 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 "string_view.h"
+
+#ifndef SOURCE_CODE_H
+#define SOURCE_CODE_H
+
+typedef struct source_code
+{
+    char *filepath;
+    string_view_t code;
+} source_code_t;
+
+#endif
diff --git a/tests/unit/parser_test.c b/tests/unit/parser_test.c
index a7c60d1..9eb56fd 100644
--- a/tests/unit/parser_test.c
+++ b/tests/unit/parser_test.c
@@ -31,15 +31,15 @@ parse_translation_unit_test(const MunitParameter params[], void *user_data_or_fi
 {
     arena_t arena = arena_new(ARENA_CAPACITY);
 
-    char *file_path = "main.0";
+    char *filepath = "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);
+    string_view_t code = { .chars = source_value, .size = strlen(source_value) };
+    lexer_init(&lexer, (source_code_t){ .code = code, .filepath = filepath });
 
     parser_t parser;
-    parser_init(&parser, &lexer, &arena, file_path);
+    parser_init(&parser, &lexer, &arena, filepath);
 
     ast_node_t *translation_unit_node = parser_parse_translation_unit(&parser);
     assert_not_null(translation_unit_node);

base-commit: 978a9914a9abc98ed9f866d528e9a094fbc1b35e
-- 
2.34.1


^ permalink raw reply	[flat|nested] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ messages in thread
* [PATCH olang v2 2/2] ast: inline ast_node_data_t union typedef
@ 2024-08-13 18:55 Johnny Richard
  2024-08-13 18:04 ` [olang/patches/.build.yml] build success builds.sr.ht
  0 siblings, 1 reply; 124+ messages in thread
From: Johnny Richard @ 2024-08-13 18:55 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/ast.c                   | 20 ++++++++++----------
 src/ast.h                   | 20 +++++++++-----------
 src/codegen_linux_aarch64.c | 10 +++++-----
 src/codegen_linux_x86_64.c  | 10 +++++-----
 src/parser.c                |  4 ++--
 src/pretty_print_ast.c      | 12 ++++++------
 tests/unit/parser_test.c    | 14 +++++++-------
 7 files changed, 44 insertions(+), 46 deletions(-)

diff --git a/src/ast.c b/src/ast.c
index 1f7df9c..aa9e6db 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -29,7 +29,7 @@ ast_new_program(arena_t *arena, ast_node_t *fn_def)
     assert(node);
 
     node->kind = AST_NODE_PROGRAM;
-    ast_program_t *program = &node->data.as_program;
+    ast_program_t *program = &node->as_program;
 
     program->fn = fn_def;
 
@@ -43,7 +43,7 @@ ast_new_node_fn_def(arena_t *arena, string_view_t identifier, type_t return_type
     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;
+    ast_fn_definition_t *fn_def = &node_fn_def->as_fn_def;
 
     fn_def->identifier = identifier;
     fn_def->return_type = return_type;
@@ -59,9 +59,9 @@ ast_new_node_bin_op(arena_t *arena, ast_binary_op_kind_t kind, ast_node_t *lhs,
     assert(node_bin_op);
 
     node_bin_op->kind = AST_NODE_BINARY_OP;
-    node_bin_op->data.as_bin_op.kind = kind;
-    node_bin_op->data.as_bin_op.lhs = lhs;
-    node_bin_op->data.as_bin_op.rhs = rhs;
+    node_bin_op->as_bin_op.kind = kind;
+    node_bin_op->as_bin_op.lhs = lhs;
+    node_bin_op->as_bin_op.rhs = rhs;
 
     return node_bin_op;
 }
@@ -73,8 +73,8 @@ ast_new_node_literal_u32(arena_t *arena, uint32_t value)
     assert(node_literal);
 
     node_literal->kind = AST_NODE_LITERAL;
-    node_literal->data.as_literal.kind = AST_LITERAL_U32;
-    node_literal->data.as_literal.as_u32 = value;
+    node_literal->as_literal.kind = AST_LITERAL_U32;
+    node_literal->as_literal.as_u32 = value;
 
     return node_literal;
 }
@@ -98,10 +98,10 @@ ast_new_node_block(arena_t *arena)
 
     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);
+    node_block->as_block.nodes = (list_t *)arena_alloc(arena, sizeof(list_t));
+    assert(node_block->as_block.nodes);
 
-    list_init(node_block->data.as_block.nodes, arena);
+    list_init(node_block->as_block.nodes, arena);
 
     return node_block;
 }
diff --git a/src/ast.h b/src/ast.h
index a58a492..024f2cc 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -106,20 +106,18 @@ typedef struct ast_return_stmt
     ast_node_t *data;
 } ast_return_stmt_t;
 
-typedef union
-{
-    ast_program_t as_program;
-    ast_fn_definition_t as_fn_def;
-    ast_binary_op_t as_bin_op;
-    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;
+    union
+    {
+        ast_program_t as_program;
+        ast_fn_definition_t as_fn_def;
+        ast_binary_op_t as_bin_op;
+        ast_literal_t as_literal;
+        ast_block_t as_block;
+        ast_return_stmt_t as_return_stmt;
+    };
 } ast_node_t;
 
 ast_node_t *
diff --git a/src/codegen_linux_aarch64.c b/src/codegen_linux_aarch64.c
index 73f4aab..dafdcc4 100644
--- a/src/codegen_linux_aarch64.c
+++ b/src/codegen_linux_aarch64.c
@@ -47,9 +47,9 @@ 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_program_t program = node->as_program;
 
-    ast_fn_definition_t fn = program.fn->data.as_fn_def;
+    ast_fn_definition_t fn = program.fn->as_fn_def;
 
     assert(string_view_eq_to_cstr(fn.identifier, "main"));
     codegen_linux_aarch64_emit_function(out, &fn);
@@ -72,18 +72,18 @@ 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;
+    ast_block_t block = block_node->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_return_stmt_t return_stmt = return_node->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;
+    ast_literal_t literal_u32 = literal_node->as_literal;
 
     assert(literal_u32.kind == AST_LITERAL_U32);
     uint32_t exit_code = literal_u32.as_u32;
diff --git a/src/codegen_linux_x86_64.c b/src/codegen_linux_x86_64.c
index 64a6b6e..7ccb252 100644
--- a/src/codegen_linux_x86_64.c
+++ b/src/codegen_linux_x86_64.c
@@ -35,9 +35,9 @@ codegen_linux_x86_64_emit_program(FILE *out, ast_node_t *node)
     codegen_linux_x86_64_emit_start_entrypoint(out);
 
     assert(node->kind == AST_NODE_PROGRAM);
-    ast_program_t program = node->data.as_program;
+    ast_program_t program = node->as_program;
 
-    ast_fn_definition_t fn = program.fn->data.as_fn_def;
+    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);
@@ -61,18 +61,18 @@ codegen_linux_x86_64_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;
+    ast_block_t block = block_node->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_return_stmt_t return_stmt = return_node->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;
+    ast_literal_t literal_u32 = literal_node->as_literal;
 
     assert(literal_u32.kind == AST_LITERAL_U32);
     uint32_t exit_code = literal_u32.as_u32;
diff --git a/src/parser.c b/src/parser.c
index 5ba5a3f..eefca01 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -336,9 +336,9 @@ parser_parse_block(parser_t *parser)
         return NULL;
     }
 
-    node_return_stmt->data.as_return_stmt.data = expr;
+    node_return_stmt->as_return_stmt.data = expr;
 
-    list_append(node_block->data.as_block.nodes, node_return_stmt);
+    list_append(node_block->as_block.nodes, node_return_stmt);
     if (!skip_expected_token(parser, TOKEN_LF)) {
         return NULL;
     }
diff --git a/src/pretty_print_ast.c b/src/pretty_print_ast.c
index 19ccafe..6ca172f 100644
--- a/src/pretty_print_ast.c
+++ b/src/pretty_print_ast.c
@@ -116,13 +116,13 @@ 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->data.as_program.fn, arena);
+            pretty_print_node_t *fn_node = ast_node_to_pretty_print_node(ast->as_program.fn, arena);
             list_append(node->children, fn_node);
             return node;
         }
         case AST_NODE_FN_DEF: {
             pretty_print_node_t *node = pretty_print_node_new(arena);
-            ast_fn_definition_t fn_def = ast->data.as_fn_def;
+            ast_fn_definition_t fn_def = ast->as_fn_def;
 
             char name[256];
             sprintf(name,
@@ -138,7 +138,7 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
         }
         case AST_NODE_BLOCK: {
             pretty_print_node_t *node = pretty_print_node_new(arena);
-            ast_block_t block = ast->data.as_block;
+            ast_block_t block = ast->as_block;
 
             node->name = "Block";
 
@@ -152,7 +152,7 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
         }
         case AST_NODE_RETURN_STMT: {
             pretty_print_node_t *node = pretty_print_node_new(arena);
-            ast_return_stmt_t return_stmt = ast->data.as_return_stmt;
+            ast_return_stmt_t return_stmt = ast->as_return_stmt;
 
             node->name = "Return_Statement";
 
@@ -163,7 +163,7 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
         }
         case AST_NODE_LITERAL: {
             pretty_print_node_t *node = pretty_print_node_new(arena);
-            ast_literal_t literal = ast->data.as_literal;
+            ast_literal_t literal = ast->as_literal;
 
             char name[256];
             switch (literal.kind) {
@@ -181,7 +181,7 @@ ast_node_to_pretty_print_node(ast_node_t *ast, arena_t *arena)
         }
         case AST_NODE_BINARY_OP: {
             pretty_print_node_t *node = pretty_print_node_new(arena);
-            ast_binary_op_t binop = ast->data.as_bin_op;
+            ast_binary_op_t binop = ast->as_bin_op;
 
             switch (binop.kind) {
                 case AST_BINOP_ADDITION: {
diff --git a/tests/unit/parser_test.c b/tests/unit/parser_test.c
index 1925a95..3cdac41 100644
--- a/tests/unit/parser_test.c
+++ b/tests/unit/parser_test.c
@@ -45,11 +45,11 @@ parse_program_test(const MunitParameter params[], void *user_data_or_fixture)
     assert_not_null(program_node);
     assert_uint(program_node->kind, ==, AST_NODE_PROGRAM);
 
-    ast_program_t program = program_node->data.as_program;
+    ast_program_t program = program_node->as_program;
     assert_not_null(program.fn);
     assert_uint(program.fn->kind, ==, AST_NODE_FN_DEF);
 
-    ast_fn_definition_t fn = program.fn->data.as_fn_def;
+    ast_fn_definition_t fn = program.fn->as_fn_def;
     assert_memory_equal(fn.identifier.size, fn.identifier.chars, "main");
     assert_uint(fn.return_type, ==, TYPE_U32);
 
@@ -57,8 +57,8 @@ parse_program_test(const MunitParameter params[], void *user_data_or_fixture)
     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_uint(list_size(block->as_block.nodes), ==, 1);
+    list_item_t *block_item = list_get(block->as_block.nodes, 0);
     assert_not_null(block_item);
     assert_not_null(block_item->value);
 
@@ -66,11 +66,11 @@ parse_program_test(const MunitParameter params[], void *user_data_or_fixture)
     assert_not_null(node);
     assert_uint(node->kind, ==, AST_NODE_RETURN_STMT);
 
-    ast_node_t *number_node = node->data.as_return_stmt.data;
+    ast_node_t *number_node = node->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.as_u32, ==, 69);
+    assert_uint(number_node->as_literal.kind, ==, AST_LITERAL_U32);
+    assert_uint(number_node->as_literal.as_u32, ==, 69);
 
     arena_free(&arena);
 
-- 
2.46.0


^ permalink raw reply	[flat|nested] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ 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; 124+ 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] 124+ messages in thread

end of thread, other threads:[~2024-11-08  4:25 UTC | newest]

Thread overview: 124+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-10-04 23:02 [PATCH olang] ast: add token location at the ast nodes Carlos Maniero
2024-10-04 23:03 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-05  1:04 ` [PATCH olang] ast: add token location at the ast nodes Johnny Richard
  -- strict thread matches above, loose matches on Subject: below --
2024-11-08  4:24 [PATCH olang v1] docs: add code highlight post processor Carlos Maniero
2024-11-08  4:24 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-11-06 11:42 [PATCH olang] docs: layout: change website look and feel Carlos Maniero
2024-11-06 11:43 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-11-06  4:11 [PATCH olang v1 2/2] docs: add the C FFI section Carlos Maniero
2024-11-06  4:12 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-31  3:13 [PATCH olang v2 6/6] ast: remove dead code from var_assign ast node Carlos Maniero
2024-10-31  3:14 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-28  2:06 [PATCH olang] docs: add script to generate man pages from docstring Carlos Maniero
2024-10-28  2:07 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-24 12:38 [PATCH olang v1 6/6] ast: remove dead code from var_assign ast node Carlos Maniero
2024-10-24 12:39 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-23  2:20 [PATCH olang v1 5/5] semantics: add scope to all nodes Carlos Maniero
2024-10-23  2:21 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-22  3:39 [PATCH olang v0] proposal: checker: set the eval type at the binary op expression Carlos Maniero
2024-10-22  3:40 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-19 14:10 [PATCH olang v2 1/1] codegen: x64: deref returns pointer value Carlos Maniero
2024-10-19 14:11 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-17  2:04 [PATCH olang v1 1/3] lexer: spec: add extern keyword for function def Johnny Richard
2024-10-17  0:07 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-17  0:46 [PATCH olang v1] codegen: allow function call at the block level Carlos Maniero
2024-10-17  0:47 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-17  0:20 [PATCH olang v1 3/3] codegen: remove linux from codegen namespace Johnny Richard
2024-10-16 22:22 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-17  0:16 [PATCH olang v1] codegen: x64: use the low bit register for 8 bits ops Carlos Maniero
2024-10-17  0:17 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-16 21:43 [PATCH olang v2] docs: info: add while-loop to getting started Johnny Richard
2024-10-16 19:44 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-16 19:41 [PATCH olang v2] spec: remove comments from the spec Carlos Maniero
2024-10-16 19:41 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-15 22:59 [PATCH olang] spec: allow comments inside blocks Carlos Maniero
2024-10-15 22:59 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-14 12:06 [PATCH olang v1 3/3] codegen: x64: generate dereference assignment Carlos Maniero
2024-10-14 12:07 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-14 11:29 [PATCH olang v1 2/2] parser: spec: allow nested unary expressions Johnny Richard
2024-10-14  9:31 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-11 16:57 [PATCH olang v2 4/4] codestyle: limit the code to 80 characters Carlos Maniero
2024-10-11 16:58 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-11 12:26 [PATCH olang v1] tests: show test name into test execution output Carlos Maniero
2024-10-11 12:26 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-11  4:02 [PATCH olang v1] build: turn make build output less noisy Johnny Richard
2024-10-11  2:04 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-11  4:02 [PATCH olang v1] build: turn make build output less noisy Johnny Richard
2024-10-11  2:03 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-11  3:24 [PATCH olang v1] spec: add contrib bin script for validating olang spec Johnny Richard
2024-10-11  1:25 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-10 16:29 [PATCH olang v3 2/2] parser: fix unary precedence Carlos Maniero
2024-10-10 16:30 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-10 12:07 [PATCH olang v2] spec: parser: enable expression on function level Carlos Maniero
2024-10-10 12:08 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-10 11:46 [PATCH olang] spec: parser: enable expression on function level Carlos Maniero
2024-10-10 11:47 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-10  1:33 [PATCH olang v1 5/6] codestyle: add trailing comma on struct initializer Carlos Maniero
2024-10-10  1:34 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-09 21:18 [PATCH olang v1 3/3] parser: codegen(x86_64): add bitwise not unary op support Johnny Richard
2024-10-09 19:35 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-09 14:53 [PATCH olang v1] docs: info: add while-loop to getting started Johnny Richard
2024-10-09 12:54 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-09 12:19 [PATCH olang] parser: parse pointer types Carlos Maniero
2024-10-09 12:19 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-09 11:28 [PATCH olang v1] parser: codegen(x86_64): docs: implement else if Johnny Richard
2024-10-09  9:29 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-09 11:24 [PATCH olang] parser: returns an unknown type instead of a SV Carlos Maniero
2024-10-09 11:24 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-08 21:33 [PATCH olang v1] parser: add support for parsing while-statements Johnny Richard
2024-10-08 19:34 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-08 16:33 [PATCH olang v1 2/2] chore: ignore *.orig file on root .gitignore Johnny Richard
2024-10-08 14:35 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-08 15:53 [PATCH olang v1] lexer: add 'while' keyword token Johnny Richard
2024-10-08 13:54 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-08 14:49 [PATCH olang v1] codegen: x86_64: emit assembly for var assignment stmts Johnny Richard
2024-10-08 12:50 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-08 13:37 [PATCH olang v1] parser: add support for parsing var assignments Johnny Richard
2024-10-08 11:38 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-08 10:01 [PATCH olang] docs: fix remove semicolumn on the example Carlos Maniero
2024-10-08 10:01 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-08  3:15 [PATCH olang] docs: introduce olang for newcomers Carlos Maniero
2024-10-08  3:15 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-07 16:29 [PATCH olang] docs: installation: add information about how to clone the project Carlos Maniero
2024-10-07 16:30 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-06 13:44 [PATCH olang v1] chore: parser: remove old unit test for parsing Johnny Richard
2024-10-06 11:45 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-05  6:59 [PATCH olang v1 2/2] codegen: x64: evaluate expression considering the data return data size Carlos Maniero
2024-10-05  7:00 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-04 22:12 [PATCH olang] lexer: create token location structure Carlos Maniero
2024-10-04 22:12 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-04 17:51 [PATCH olang v1] lexer: add lexer cursor abstraction Johnny Richard
2024-10-04 15:52 ` [olang/patches/.build.yml] build success builds.sr.ht
2024-10-04 16:34 [PATCH olang] lexer: add source code abstraction Carlos Maniero
2024-10-04 16:34 ` [olang/patches/.build.yml] build success builds.sr.ht
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-08-13 18:55 [PATCH olang v2 2/2] ast: inline ast_node_data_t union typedef Johnny Richard
2024-08-13 18:04 ` [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