public inbox for ~johnnyrichard/olang-devel@lists.sr.ht
 help / color / mirror / code / Atom feed
* [PATCH olang v1 0/2] Introduce CLI option for improved AST tree visualization
@ 2024-03-25 22:36 Johnny Richard
  2024-03-25 22:36 ` [PATCH olang v1 1/2] cli: add new option to pretty print AST tree Johnny Richard
  2024-03-25 22:36 ` [PATCH olang v1 2/2] cli: remove code duplication Johnny Richard
  0 siblings, 2 replies; 31+ messages in thread
From: Johnny Richard @ 2024-03-25 22:36 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Johnny Richard (2):
  cli: add new option to pretty print AST tree
  cli: remove code duplication

 docs/manpages/olang.md |   4 +
 src/cli.c              |   5 +-
 src/cli.h              |   3 +-
 src/main.c             |  34 ++++-
 src/pretty_print_ast.c | 286 +++++++++++++++++++++++++++++++++++++++++
 src/pretty_print_ast.h |  25 ++++
 6 files changed, 351 insertions(+), 6 deletions(-)
 create mode 100644 src/pretty_print_ast.c
 create mode 100644 src/pretty_print_ast.h


base-commit: ce5389c3835d0222a4f5f74c27f6bd06bbdd93c7
-- 
2.44.0


^ permalink raw reply	[flat|nested] 31+ messages in thread
* [PATCH olang v1 2/2] ast: inline ast_node_data_t union definition
@ 2024-08-13 18:16 Johnny Richard
  2024-08-13 17:27 ` [olang/patches/.build.yml] build failed builds.sr.ht
  0 siblings, 1 reply; 31+ messages in thread
From: Johnny Richard @ 2024-08-13 18:16 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  | 12 ++++++------
 src/parser.c                |  4 ++--
 src/pretty_print_ast.c      | 12 ++++++------
 tests/unit/parser_test.c    | 14 +++++++-------
 7 files changed, 45 insertions(+), 47 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 28e7f8e..64ec0e0 100644
--- a/src/codegen_linux_x86_64.c
+++ b/src/codegen_linux_x86_64.c
@@ -38,9 +38,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);
@@ -70,7 +70,7 @@ codegen_linux_x86_64_emit_expression(FILE *out, ast_node_t *expr_node)
 {
     switch (expr_node->kind) {
         case AST_NODE_LITERAL: {
-            ast_literal_t literal_u32 = expr_node->data.as_literal;
+            ast_literal_t literal_u32 = expr_node->as_literal;
             assert(literal_u32.kind == AST_LITERAL_U32);
             uint32_t n = literal_u32.as_u32;
 
@@ -78,7 +78,7 @@ codegen_linux_x86_64_emit_expression(FILE *out, ast_node_t *expr_node)
             return;
         }
         case AST_NODE_BINARY_OP: {
-            ast_binary_op_t bin_op = expr_node->data.as_bin_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);
@@ -305,14 +305,14 @@ 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 *expr = return_stmt.data;
 
diff --git a/src/parser.c b/src/parser.c
index 5dd4ef1..24094b3 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] 31+ messages in thread
* [PATCH olang v1] build: rename linter to format to avoid confusion
@ 2024-04-20 13:54 Johnny Richard
  2024-04-20 12:57 ` [olang/patches/.build.yml] build failed builds.sr.ht
  0 siblings, 1 reply; 31+ messages in thread
From: Johnny Richard @ 2024-04-20 13:54 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

We have been using linter as the target to check our code formatting.
Today we are using `clang-format` to verify the formatting.  This tool
does only formatting, and miss other features that a linter have.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 .build.yml                 |  4 ++--
 Makefile                   | 16 ++++++++--------
 docs/pages/contribute.md   | 16 ++++++++--------
 tests/integration/Makefile |  8 ++++----
 tests/unit/Makefile        |  8 ++++----
 5 files changed, 26 insertions(+), 26 deletions(-)

diff --git a/.build.yml b/.build.yml
index 72f0254..01f4aab 100644
--- a/.build.yml
+++ b/.build.yml
@@ -11,9 +11,9 @@ environment:
 sources:
   - https://git.sr.ht/~johnnyrichard/olang
 tasks:
-  - lint: |
+  - format: |
       cd olang
-      make linter
+      make format
   - build: |
       cd olang
       make
diff --git a/Makefile b/Makefile
index cdfc8e1..27337d4 100644
--- a/Makefile
+++ b/Makefile
@@ -16,17 +16,17 @@ $(TARGET): $(BUILD_DIR) $(OBJS)
 $(BUILD_DIR):
 	@mkdir -p $@
 
-.PHONY: linter
-linter: $(SRCS) $(HEADERS)
+.PHONY: format
+format: $(SRCS) $(HEADERS)
 	clang-format --dry-run --Werror $?
-	$(MAKE) -C tests/integration/ linter
-	$(MAKE) -C tests/unit/ linter
+	$(MAKE) -C tests/integration/ format
+	$(MAKE) -C tests/unit/ format
 
-.PHONY: linter-fix
-linter-fix: $(SRCS) $(HEADERS)
+.PHONY: format-fix
+format-fix: $(SRCS) $(HEADERS)
 	clang-format -i $?
-	$(MAKE) -C tests/integration/ linter-fix
-	$(MAKE) -C tests/unit/ linter-fix
+	$(MAKE) -C tests/integration/ format-fix
+	$(MAKE) -C tests/unit/ format-fix
 
 .PHONY: integration-test
 integration-test:
diff --git a/docs/pages/contribute.md b/docs/pages/contribute.md
index 884c4b4..a6dfd04 100644
--- a/docs/pages/contribute.md
+++ b/docs/pages/contribute.md
@@ -25,21 +25,21 @@ 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
+Please refer to the **Format** section below for guidance on its
 application.
 
-### Linter
+### Format
 
-Checking for linter issues:
+Checking for format issues:
 
 ``` {.sh}
-make linter
+make format
 ```
 
 Most of the common code style mistakes are fixed by:
 
 ``` {.sh}
-make linter-fix
+make format-fix
 ```
 
 ### .editorconfig
@@ -92,9 +92,9 @@ the email-driven workflow here, but you can check it out at
 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.
+    - If the tests or the format check 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
 
diff --git a/tests/integration/Makefile b/tests/integration/Makefile
index db2b7d9..4625707 100644
--- a/tests/integration/Makefile
+++ b/tests/integration/Makefile
@@ -16,12 +16,12 @@ all: $(MUNIT) proc_exec.o cli_runner.o $(TESTS)
 clean:
 	$(RM) *.o *_test
 
-.PHONY: linter
-linter: $(SRCS)
+.PHONY: format
+format: $(SRCS)
 	clang-format --dry-run --Werror $?
 
-.PHONY: linter-fix
-linter-fix: $(SRCS)
+.PHONY: format-fix
+format-fix: $(SRCS)
 	clang-format -i $?
 
 cli_test: $(MUNIT) proc_exec.o cli_runner.o cli_test.o
diff --git a/tests/unit/Makefile b/tests/unit/Makefile
index 498bf98..783225c 100644
--- a/tests/unit/Makefile
+++ b/tests/unit/Makefile
@@ -18,12 +18,12 @@ clean:
 	$(RM) *.o *_test
 	$(RM) -rfv lib
 
-.PHONY: linter
-linter: $(SRCS)
+.PHONY: format
+format: $(SRCS)
 	clang-format --dry-run --Werror $?
 
-.PHONY: linter-fix
-linter-fix: $(SRCS)
+.PHONY: format-fix
+format-fix: $(SRCS)
 	clang-format -i $?
 
 %_test: $(MUNIT) $(SUBJECT_OBJS) %_test.c

base-commit: 36b028f712ff2402761ea307467860c346d3c0a0
-- 
2.44.0


^ permalink raw reply	[flat|nested] 31+ messages in thread
* [PATCH olang v1] fe: lexer: add single line comments support
@ 2024-03-28 15:58 Johnny Richard
  2024-03-28 14:59 ` [olang/patches/.build.yml] build failed builds.sr.ht
  0 siblings, 1 reply; 31+ messages in thread
From: Johnny Richard @ 2024-03-28 15:58 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 examples/main_exit.ol        |  3 +++
 src/lexer.c                  |  7 +++++++
 src/parser.c                 |  1 +
 tests/integration/cli_test.c | 31 +++++++++++++++++--------------
 4 files changed, 28 insertions(+), 14 deletions(-)

diff --git a/examples/main_exit.ol b/examples/main_exit.ol
index c86fc68..8952017 100644
--- a/examples/main_exit.ol
+++ b/examples/main_exit.ol
@@ -1,3 +1,6 @@
+# Expected:
+# - output: "" 
+
 fn main(): u32 {
   return 0
 }
diff --git a/src/lexer.c b/src/lexer.c
index 801e4d0..684cad1 100644
--- a/src/lexer.c
+++ b/src/lexer.c
@@ -76,6 +76,13 @@ lexer_next_token(lexer_t *lexer, token_t *token)
     }
 
     while (lexer_is_not_eof(lexer)) {
+        if (current_char == '#') {
+            while (current_char != '\n' && lexer_is_not_eof(lexer)) {
+                lexer_skip_char(lexer);
+                current_char = lexer_current_char(lexer);
+            }
+        }
+
         if (isalpha(current_char)) {
             size_t start_offset = lexer->offset;
             while (isalnum(current_char) && lexer_is_not_eof(lexer)) {
diff --git a/src/parser.c b/src/parser.c
index 76ef91a..b800870 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -62,6 +62,7 @@ parser_init(parser_t *parser, lexer_t *lexer, arena_t *arena, char *file_path)
 ast_node_t *
 parser_parse_program(parser_t *parser)
 {
+    skip_line_feeds(parser->lexer);
     ast_node_t *fn = parser_parse_fn_definition(parser);
     if (fn == NULL) {
         return NULL;
diff --git a/tests/integration/cli_test.c b/tests/integration/cli_test.c
index d46471b..e7ae059 100644
--- a/tests/integration/cli_test.c
+++ b/tests/integration/cli_test.c
@@ -25,20 +25,23 @@ test_cli_dump_tokens_example_main_exit(const MunitParameter params[], void *user
     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.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");
+                              "../../examples/main_exit.ol:1:12: <line_feed>\n"
+                              "../../examples/main_exit.ol:2:16: <line_feed>\n"
+                              "../../examples/main_exit.ol:3:1: <line_feed>\n"
+                              "../../examples/main_exit.ol:4:1: <fn>\n"
+                              "../../examples/main_exit.ol:4:4: <identifier>\n"
+                              "../../examples/main_exit.ol:4:8: <(>\n"
+                              "../../examples/main_exit.ol:4:9: <)>\n"
+                              "../../examples/main_exit.ol:4:10: <:>\n"
+                              "../../examples/main_exit.ol:4:12: <identifier>\n"
+                              "../../examples/main_exit.ol:4:16: <{>\n"
+                              "../../examples/main_exit.ol:4:17: <line_feed>\n"
+                              "../../examples/main_exit.ol:5:3: <return>\n"
+                              "../../examples/main_exit.ol:5:10: <number>\n"
+                              "../../examples/main_exit.ol:5:11: <line_feed>\n"
+                              "../../examples/main_exit.ol:6:1: <}>\n"
+                              "../../examples/main_exit.ol:6:2: <line_feed>\n"
+                              "../../examples/main_exit.ol:7:1: <EOF>\n");
     return MUNIT_OK;
 }
 

base-commit: 117a06874c48c64e8ad4befbab244670f4f9ca9c
-- 
2.44.0


^ permalink raw reply	[flat|nested] 31+ messages in thread
* [PATCH olang v1 2/2] docs: spec: add variables and constants specification
@ 2024-03-27  3:21 Carlos Maniero
  2024-03-27  3:22 ` [olang/patches/.build.yml] build failed builds.sr.ht
  0 siblings, 1 reply; 31+ messages in thread
From: Carlos Maniero @ 2024-03-27  3:21 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
  x = 2

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

Variables and Linkage:
----------------------

Variables can be defined globally or within a function. Variables
defined globally are added to the *.data* section. On the other hand,
variables defined within a function utilize the stack. Currently, the
specification does not provide a keyword for external linkage yet, hence
variables cannot be accessed outside their translation unit.

Constants and Linkage:
----------------------

Constants behave similarly to variables. Constants defined at the file
level are added to the *.rodata* section. Constants within functions are
subject to semantic checks. Attempting to bypass these checks to
reassign a global constant will result in a segmentation fault
(SEGFAULT) and will be accepted at the function level.

Example of bypassing semantic checks in C:

  int a = 1;
  const int b = 2;
  int *c = &a + 1;
  *c = 3;
  assert(b == 3); // its true

Static variables in function level
----------------------------------

In C, static variables can be defined within the scope of a function.
These variables are added to the `*.data*` segment and are only
accessible within the function where they are defined. This is a unique
behavior of C that some might find unusual.

However, olang does not support this feature. In olang, if you need a
variable that retains its value across function calls (like a static
variable in C), you must define it at the file level.

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

diff --git a/docs/pages/language-specification.md b/docs/pages/language-specification.md
index 4541ba8..708b679 100644
--- a/docs/pages/language-specification.md
+++ b/docs/pages/language-specification.md
@@ -24,7 +24,15 @@ language.
 (* Entry Point *)
 <translation-unit>    ::= <ows> (<global-statements> <end-of-statement>)*
 
-<global-statements>   ::= <function-definition>
+<global-statements>   ::= <function-definition> | <variable-definition> | <variable-reassign> | <const-definition>
+
+(* Variables *)
+<variable-definition> ::= 'var' <ws> <variable-name> <ows> ':' <ows> <type> <ows> <variable-assign>?
+<const-definition>    ::= 'const' <ws> <variable-name> <ows> ':' <ows> <type> <ows> <variable-assign>
+
+<variable-name>       ::= <identifier>
+<variable-assign>     ::= '=' <ows> <expression>
+<variable-reassign>   ::= <variable-name> <ows> <variable-assign> <end-of-statement>
 
 (* Functions *)
 <function-definition> ::= 'fn' <ws> <function-name> <ows>
@@ -38,11 +46,11 @@ language.
 <block>               ::= '{' <ows> <statement> <ows> (<end-of-statement>
 <ows> <statement> <ows>)* <end-of-statement>? <ows> '}'
 <end-of-statement>    ::= ';' | <line-break> | <end-of-file>
-<statement>           ::= <return-statement>
+<statement>           ::= <return-statement> | <variable-definition> | <variable-reassign> | <const-definition>
 <return-statement>    ::= 'return' <ws> <expression>
 
 (* Expressions *)
-<expression>          ::= <integer>
+<expression>          ::= <integer> | <identifier>
 
 (* Identifiers *)
 <type>                ::= 'u32'
-- 
2.34.1


^ permalink raw reply	[flat|nested] 31+ messages in thread
* [PATCH olang v3] refactor: rename zero programming language to olang
@ 2024-03-13 12:32 Fabio Maciel
  2024-03-13 12:33 ` [olang/patches/.build.yml] build failed builds.sr.ht
  0 siblings, 1 reply; 31+ messages in thread
From: Fabio Maciel @ 2024-03-13 12:32 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>
---
 V3 fix hacking.md
 .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..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/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] 31+ messages in thread
* [PATCH olang] parser: abort when parser identifies a syntax error
@ 2024-03-08 20:52 Johnny Richard
  2024-03-08 19:54 ` [olang/patches/.build.yml] build failed builds.sr.ht
  0 siblings, 1 reply; 31+ messages in thread
From: Johnny Richard @ 2024-03-08 20:52 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

The current implementation fails with segfault when the parser finds a
syntax error.  We are not prepared to recover from errors at this
moment.

This patch aborts when found an expected token and it also improves the
error output by showing the wrong token string value.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
Let's postpone syntax error recovery.  I want to keep things simple
right now.

 src/parser.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/parser.c b/src/parser.c
index a9699be..cfea9ef 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -173,18 +173,18 @@ expected_token(parser_t *parser, token_t *token, token_kind_t expected_kind)
 
     if (token->kind != expected_kind) {
         fprintf(stderr,
-                "%s:%lu:%lu: error: got <%s> token but expect <%s>\n",
+                "%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_kind_to_cstr(token->kind),
+                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), "^");
 
-        return false;
+        exit(EXIT_FAILURE);
     }
     return true;
 }

base-commit: 35f594370443a2b9f73d2d2ebe573b4cab472be6
-- 
2.44.0


^ permalink raw reply	[flat|nested] 31+ messages in thread
* [PATCH olang v2 3/3] cli: add compilation -o option with --save-temps
@ 2024-03-05  8:44 Johnny Richard
  2024-03-05  7:51 ` [olang/patches/.build.yml] build failed builds.sr.ht
  0 siblings, 1 reply; 31+ messages in thread
From: Johnny Richard @ 2024-03-05  8:44 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

In order to compile the program, we are introduction the option -o to
the compiler.

This implementation is very similar to the gcc one.  Since the program
is made of a single file, no need to over complicate the compilation
with multiple files.

The option --save-temps is used to keep files used to create the binary
file (.a and .o).  By default this option is disabled.

WARNING
-------

This implementation relies on "as" (GNU Assembler) and the "ld" (GNU
linker) command.  Make sure you have it install before trying to compile
any program.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
v1 -> v2: Change file_path type from string_view_t to cstr.
          Use first bit for enum cli_opt_t.
          Change cli_opts_t#prog -> cli_opts_t#compiler_path.
          Fix assert on args.
          Refactor creation of cstr asm_file.

 docs/manpages/0c.md |  13 +++-
 src/main.c          | 164 ++++++++++++++++++++++++++++++++++++--------
 2 files changed, 146 insertions(+), 31 deletions(-)

diff --git a/docs/manpages/0c.md b/docs/manpages/0c.md
index 87a56df..e3d3cfc 100644
--- a/docs/manpages/0c.md
+++ b/docs/manpages/0c.md
@@ -4,11 +4,14 @@
 
 # NAME
 
-0c - zero langague compiler
+0c - zero language compiler
 
 # SYNOPSIS
 
-**0c** **----dump-tokens** source.0
+**0c**
+    source_file
+    [**----dump-tokens**]
+    [**--o** output_file [**----save-temps**]] 
 
 # DESCRIPTION
 
@@ -19,3 +22,9 @@ contains utilities to help the language development.
 
 **----dump-tokens**
 :   Display lexical tokens given a soruce.0 code.
+
+**--o <file>**
+:  Compile program into a binary file
+
+**----save-temps**
+:   Keep temp files used to compile program
diff --git a/src/main.c b/src/main.c
index 2b2f12a..e4be5f7 100644
--- a/src/main.c
+++ b/src/main.c
@@ -22,7 +22,9 @@
 #include <string.h>
 
 #include "arena.h"
+#include "codegen_linux_x86_64.h"
 #include "lexer.h"
+#include "parser.h"
 #include "string_view.h"
 
 // TODO: find a better solution to define the arena capacity
@@ -37,14 +39,29 @@ typedef struct cli_args
 char *
 cli_args_shift(cli_args_t *args);
 
+typedef enum
+{
+    CLI_OPT_DUMP_TOKENS = 1 << 0,
+    CLI_OPT_OUTPUT = 1 << 1,
+    CLI_OPT_SAVE_TEMPS = 1 << 2
+} cli_opt_t;
+
 typedef struct cli_opts
 {
-    bool dump_tokens;
+    uint32_t options;
+    char *compiler_path;
     char *file_path;
+    string_view_t output_bin;
 } cli_opts_t;
 
 void
-print_usage(FILE *stream, char *prog);
+print_usage(FILE *stream, char *compiler_path);
+
+void
+handle_dump_tokens(cli_opts_t *opts);
+
+void
+handle_codegen_linux_x86_64(cli_opts_t *opts);
 
 static void
 print_token(char *file_path, token_t *token);
@@ -52,34 +69,64 @@ print_token(char *file_path, token_t *token);
 string_view_t
 read_entire_file(char *file_path, arena_t *arena);
 
+void
+cli_opts_parse_output(cli_opts_t *opts, cli_args_t *args);
+
 int
 main(int argc, char **argv)
 {
     cli_args_t args = { .argc = argc, .argv = argv };
     cli_opts_t opts = { 0 };
 
-    char *prog = cli_args_shift(&args);
+    opts.compiler_path = 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)) {
+    char *arg = cli_args_shift(&args);
+    while (arg != NULL) {
         if (strcmp(arg, "--dump-tokens") == 0) {
-            opts.dump_tokens = true;
+            opts.options |= CLI_OPT_DUMP_TOKENS;
+        } else if (strcmp(arg, "--save-temps") == 0) {
+            opts.options |= CLI_OPT_SAVE_TEMPS;
+        } else if (strcmp(arg, "-o") == 0) {
+            cli_opts_parse_output(&opts, &args);
         } else {
             opts.file_path = arg;
         }
+        arg = cli_args_shift(&args);
+    }
+
+    if (opts.options & CLI_OPT_OUTPUT) {
+        handle_codegen_linux_x86_64(&opts);
+        return EXIT_SUCCESS;
     }
 
-    if (!opts.dump_tokens) {
-        print_usage(stderr, prog);
-        return EXIT_FAILURE;
+    if (opts.options & CLI_OPT_DUMP_TOKENS) {
+        handle_dump_tokens(&opts);
+        return EXIT_SUCCESS;
+    }
+
+    print_usage(stderr, opts.compiler_path);
+    return EXIT_FAILURE;
+}
+
+char *
+cli_args_shift(cli_args_t *args)
+{
+    if (args->argc == 0)
+        return NULL;
+    --(args->argc);
+    return *(args->argv)++;
+}
+
+void
+handle_dump_tokens(cli_opts_t *opts)
+{
+    if (opts->file_path == NULL) {
+        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);
+    string_view_t file_content = read_entire_file(opts->file_path, &arena);
 
     lexer_t lexer = { 0 };
     lexer_init(&lexer, file_content);
@@ -87,29 +134,70 @@ main(int argc, char **argv)
     token_t token = { 0 };
     lexer_next_token(&lexer, &token);
     while (token.kind != TOKEN_EOF) {
-        print_token(opts.file_path, &token);
+        print_token(opts->file_path, &token);
         lexer_next_token(&lexer, &token);
     }
-    print_token(opts.file_path, &token);
+    print_token(opts->file_path, &token);
 
-    free(file_content.chars);
-
-    return EXIT_SUCCESS;
+    arena_free(&arena);
 }
 
-char *
-cli_args_shift(cli_args_t *args)
+void
+handle_codegen_linux_x86_64(cli_opts_t *opts)
 {
-    if (args->argc == 0)
-        return NULL;
-    --(args->argc);
-    return *(args->argv)++;
+    if (opts->file_path == NULL) {
+        print_usage(stderr, opts->compiler_path);
+        exit(EXIT_FAILURE);
+    }
+
+    arena_t arena = arena_new(ARENA_CAPACITY);
+    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);
+
+    ast_node_t *ast = parser_parse_fn_definition(&parser);
+
+    char asm_file[opts->output_bin.size + 3];
+    sprintf(asm_file, "" SV_FMT ".s", SV_ARG(opts->output_bin));
+
+    FILE *out = fopen(asm_file, "w");
+    assert(out);
+    codegen_linux_x86_64_emit_program(out, ast);
+    fclose(out);
+
+    char command[512];
+    sprintf(command, "as %s -o " SV_FMT ".o", asm_file, SV_ARG(opts->output_bin));
+    system(command);
+
+    sprintf(command, "ld " SV_FMT ".o -o " SV_FMT "", SV_ARG(opts->output_bin), SV_ARG(opts->output_bin));
+    system(command);
+
+    if (!(opts->options & CLI_OPT_SAVE_TEMPS)) {
+        char output_file[256];
+
+        sprintf(output_file, "" SV_FMT ".s", SV_ARG(opts->output_bin));
+        remove(output_file);
+
+        sprintf(output_file, "" SV_FMT ".o", SV_ARG(opts->output_bin));
+        remove(output_file);
+    }
+
+    arena_free(&arena);
 }
 
 void
-print_usage(FILE *stream, char *prog)
+print_usage(FILE *stream, char *compiler_path)
 {
-    fprintf(stream, "usage: %s <source.0> --dump-tokens\n", prog);
+    fprintf(stream,
+            "Usage: %s [options] file...\n"
+            "Options:\n"
+            "  --dump-tokens\t\tDisplay lexer token stream\n"
+            "  -o <file>\t\tCompile program into a binary file\n"
+            "  --save-temps\t\tKeep temp files used to compile program\n",
+            compiler_path);
 }
 
 string_view_t
@@ -118,7 +206,7 @@ read_entire_file(char *file_path, arena_t *arena)
     FILE *stream = fopen(file_path, "rb");
 
     if (stream == NULL) {
-        fprintf(stderr, "Could not open file %s: %s\n", file_path, strerror(errno));
+        fprintf(stderr, "error: could not open file %s: %s\n", file_path, strerror(errno));
         exit(EXIT_FAILURE);
     }
 
@@ -133,7 +221,7 @@ read_entire_file(char *file_path, arena_t *arena)
     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));
+        fprintf(stderr, "error: could not read file %s: %s\n", file_path, strerror(errno));
         exit(EXIT_FAILURE);
     }
 
@@ -143,6 +231,24 @@ read_entire_file(char *file_path, arena_t *arena)
     return file_content;
 }
 
+void
+cli_opts_parse_output(cli_opts_t *opts, cli_args_t *args)
+{
+    assert(opts && "opts is required");
+    assert(args && "args is required");
+
+    char *output_bin = cli_args_shift(args);
+
+    if (output_bin == NULL) {
+        fprintf(stderr, "error: missing filename after '-o'\n");
+        print_usage(stderr, opts->compiler_path);
+        exit(EXIT_FAILURE);
+    }
+
+    opts->options |= CLI_OPT_OUTPUT;
+    opts->output_bin = string_view_from_cstr(output_bin);
+}
+
 static void
 print_token(char *file_path, token_t *token)
 {
-- 
2.44.0


^ permalink raw reply	[flat|nested] 31+ messages in thread
* [PATCH olang v1 3/3] cli: add compilation -o option with --save-temps
@ 2024-03-04 19:23 Johnny Richard
  2024-03-04 18:33 ` [olang/patches/.build.yml] build failed builds.sr.ht
  0 siblings, 1 reply; 31+ messages in thread
From: Johnny Richard @ 2024-03-04 19:23 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

In order to compile the program, we are introduction the option -o to
the compiler.

This implementation is very similar to the gcc one.  Since the program
is made of a single file, no need to over complicate the compilation
with multiple files.

The option --save-temps is used to keep files used to create the binary
file (.a and .o).  By default this option is disabled.

WARNING
-------

This implementation relies on "as" (GNU Assembler) and the "ld" (GNU
linker) command.  Make sure you have it install before trying to compile
any program.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 docs/manpages/0c.md |  13 +++-
 src/main.c          | 180 +++++++++++++++++++++++++++++++++++---------
 2 files changed, 157 insertions(+), 36 deletions(-)

diff --git a/docs/manpages/0c.md b/docs/manpages/0c.md
index 87a56df..e3d3cfc 100644
--- a/docs/manpages/0c.md
+++ b/docs/manpages/0c.md
@@ -4,11 +4,14 @@
 
 # NAME
 
-0c - zero langague compiler
+0c - zero language compiler
 
 # SYNOPSIS
 
-**0c** **----dump-tokens** source.0
+**0c**
+    source_file
+    [**----dump-tokens**]
+    [**--o** output_file [**----save-temps**]] 
 
 # DESCRIPTION
 
@@ -19,3 +22,9 @@ contains utilities to help the language development.
 
 **----dump-tokens**
 :   Display lexical tokens given a soruce.0 code.
+
+**--o <file>**
+:  Compile program into a binary file
+
+**----save-temps**
+:   Keep temp files used to compile program
diff --git a/src/main.c b/src/main.c
index 2b2f12a..b27715a 100644
--- a/src/main.c
+++ b/src/main.c
@@ -22,7 +22,9 @@
 #include <string.h>
 
 #include "arena.h"
+#include "codegen_linux_x86_64.h"
 #include "lexer.h"
+#include "parser.h"
 #include "string_view.h"
 
 // TODO: find a better solution to define the arena capacity
@@ -37,20 +39,38 @@ typedef struct cli_args
 char *
 cli_args_shift(cli_args_t *args);
 
+typedef enum
+{
+    CLI_OPT_DUMP_TOKENS = 1 << 1,
+    CLI_OPT_OUTPUT = 1 << 2,
+    CLI_OPT_SAVE_TEMPS = 1 << 3
+} cli_opt;
+
 typedef struct cli_opts
 {
-    bool dump_tokens;
-    char *file_path;
+    uint32_t options;
+    string_view_t prog;
+    string_view_t file_path;
+    string_view_t output_bin;
 } cli_opts_t;
 
 void
-print_usage(FILE *stream, char *prog);
+print_usage(FILE *stream, string_view_t prog);
+
+void
+handle_dump_tokens(cli_opts_t *opts);
+
+void
+handle_codegen_linux_x86_64(cli_opts_t *opts);
 
 static void
 print_token(char *file_path, token_t *token);
 
 string_view_t
-read_entire_file(char *file_path, arena_t *arena);
+read_entire_file(string_view_t file_path, arena_t *arena);
+
+void
+cli_opts_parse_output(cli_opts_t *opts, cli_args_t *args);
 
 int
 main(int argc, char **argv)
@@ -58,28 +78,58 @@ main(int argc, char **argv)
     cli_args_t args = { .argc = argc, .argv = argv };
     cli_opts_t opts = { 0 };
 
-    char *prog = cli_args_shift(&args);
+    opts.prog = string_view_from_cstr(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)) {
+    char *arg = cli_args_shift(&args);
+    while (arg != NULL) {
         if (strcmp(arg, "--dump-tokens") == 0) {
-            opts.dump_tokens = true;
+            opts.options |= CLI_OPT_DUMP_TOKENS;
+            arg = cli_args_shift(&args);
+        } else if (strcmp(arg, "--save-temps") == 0) {
+            opts.options |= CLI_OPT_SAVE_TEMPS;
+            arg = cli_args_shift(&args);
+        } else if (strcmp(arg, "-o") == 0) {
+            cli_opts_parse_output(&opts, &args);
+            arg = cli_args_shift(&args);
         } else {
-            opts.file_path = arg;
+            opts.file_path = string_view_from_cstr(arg);
+            arg = cli_args_shift(&args);
         }
     }
 
-    if (!opts.dump_tokens) {
-        print_usage(stderr, prog);
-        return EXIT_FAILURE;
+    if (opts.options & CLI_OPT_OUTPUT) {
+        handle_codegen_linux_x86_64(&opts);
+        return EXIT_SUCCESS;
+    }
+
+    if (opts.options & CLI_OPT_DUMP_TOKENS) {
+        handle_dump_tokens(&opts);
+        return EXIT_SUCCESS;
+    }
+
+    print_usage(stderr, opts.prog);
+    return EXIT_FAILURE;
+}
+
+char *
+cli_args_shift(cli_args_t *args)
+{
+    if (args->argc == 0)
+        return NULL;
+    --(args->argc);
+    return *(args->argv)++;
+}
+
+void
+handle_dump_tokens(cli_opts_t *opts)
+{
+    if (opts->file_path.chars == NULL) {
+        print_usage(stderr, opts->prog);
+        exit(EXIT_FAILURE);
     }
 
     arena_t arena = arena_new(ARENA_CAPACITY);
-    string_view_t file_content = read_entire_file(opts.file_path, &arena);
+    string_view_t file_content = read_entire_file(opts->file_path, &arena);
 
     lexer_t lexer = { 0 };
     lexer_init(&lexer, file_content);
@@ -87,38 +137,82 @@ main(int argc, char **argv)
     token_t token = { 0 };
     lexer_next_token(&lexer, &token);
     while (token.kind != TOKEN_EOF) {
-        print_token(opts.file_path, &token);
+        print_token(opts->file_path.chars, &token);
         lexer_next_token(&lexer, &token);
     }
-    print_token(opts.file_path, &token);
+    print_token(opts->file_path.chars, &token);
 
-    free(file_content.chars);
-
-    return EXIT_SUCCESS;
+    arena_free(&arena);
 }
 
-char *
-cli_args_shift(cli_args_t *args)
+void
+handle_codegen_linux_x86_64(cli_opts_t *opts)
 {
-    if (args->argc == 0)
-        return NULL;
-    --(args->argc);
-    return *(args->argv)++;
+    if (opts->file_path.chars == NULL) {
+        print_usage(stderr, opts->prog);
+        exit(EXIT_FAILURE);
+    }
+
+    arena_t arena = arena_new(ARENA_CAPACITY);
+    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.chars);
+
+    ast_node_t *ast = parser_parse_fn_definition(&parser);
+
+    string_view_t asm_ext = string_view_from_cstr(".s");
+    char asm_file[opts->output_bin.size + asm_ext.size + 1];
+    memcpy(asm_file, opts->output_bin.chars, opts->output_bin.size);
+    memcpy(asm_file + opts->output_bin.size, asm_ext.chars, asm_ext.size);
+    asm_file[opts->output_bin.size + asm_ext.size] = 0;
+
+    FILE *out = fopen(asm_file, "w");
+    assert(out);
+    codegen_linux_x86_64_emit_program(out, ast);
+    fclose(out);
+
+    char command[512];
+    sprintf(command, "as %s -o " SV_FMT ".o", asm_file, SV_ARG(opts->output_bin));
+    system(command);
+
+    sprintf(command, "ld " SV_FMT ".o -o " SV_FMT "", SV_ARG(opts->output_bin), SV_ARG(opts->output_bin));
+    system(command);
+
+    if (!(opts->options & CLI_OPT_SAVE_TEMPS)) {
+        char output_file[256];
+
+        sprintf(output_file, "" SV_FMT ".s", SV_ARG(opts->output_bin));
+        remove(output_file);
+
+        sprintf(output_file, "" SV_FMT ".o", SV_ARG(opts->output_bin));
+        remove(output_file);
+    }
+
+    arena_free(&arena);
 }
 
 void
-print_usage(FILE *stream, char *prog)
+print_usage(FILE *stream, string_view_t prog)
 {
-    fprintf(stream, "usage: %s <source.0> --dump-tokens\n", prog);
+    fprintf(stream,
+            "Usage: " SV_FMT " [options] file...\n"
+            "Options:\n"
+            "  --dump-tokens\t\tDisplay lexer token stream\n"
+            "  -o <file>\t\tCompile program into a binary file\n"
+            "  --save-temps\t\tKeep temp files used to compile program\n",
+            SV_ARG(prog));
 }
 
 string_view_t
-read_entire_file(char *file_path, arena_t *arena)
+read_entire_file(string_view_t file_path, arena_t *arena)
 {
-    FILE *stream = fopen(file_path, "rb");
+    FILE *stream = fopen(file_path.chars, "rb");
 
     if (stream == NULL) {
-        fprintf(stderr, "Could not open file %s: %s\n", file_path, strerror(errno));
+        fprintf(stderr, "error: could not open file " SV_FMT ": %s\n", SV_ARG(file_path), strerror(errno));
         exit(EXIT_FAILURE);
     }
 
@@ -133,7 +227,7 @@ read_entire_file(char *file_path, arena_t *arena)
     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));
+        fprintf(stderr, "Could not read file " SV_FMT ": %s\n", SV_ARG(file_path), strerror(errno));
         exit(EXIT_FAILURE);
     }
 
@@ -143,6 +237,24 @@ read_entire_file(char *file_path, arena_t *arena)
     return file_content;
 }
 
+void
+cli_opts_parse_output(cli_opts_t *opts, cli_args_t *args)
+{
+    assert(opts && "opts is required");
+    assert(opts && "args is required");
+
+    char *output_bin = cli_args_shift(args);
+
+    if (output_bin == NULL) {
+        fprintf(stderr, "error: missing filename after '-o'\n");
+        print_usage(stderr, opts->prog);
+        exit(EXIT_FAILURE);
+    }
+
+    opts->options |= CLI_OPT_OUTPUT;
+    opts->output_bin = string_view_from_cstr(output_bin);
+}
+
 static void
 print_token(char *file_path, token_t *token)
 {
-- 
2.44.0


^ permalink raw reply	[flat|nested] 31+ messages in thread
* [PATCH olang] string_view: fix stack buffer overflow on to_u32
@ 2024-03-02 20:01 Johnny Richard
  2024-03-02 19:02 ` [olang/patches/.build.yml] build failed builds.sr.ht
  0 siblings, 1 reply; 31+ messages in thread
From: Johnny Richard @ 2024-03-02 20:01 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 src/string_view.c             | 2 +-
 tests/unit/string_view_test.c | 4 ++++
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/string_view.c b/src/string_view.c
index 084f417..646dabd 100644
--- a/src/string_view.c
+++ b/src/string_view.c
@@ -40,7 +40,7 @@ uint32_t
 string_view_to_u32(string_view_t str)
 {
     char ret[str.size + 1];
-    ret[str.size + 1] = 0;
+    ret[str.size] = 0;
     memcpy(ret, str.chars, str.size);
     return atoi(ret);
 }
diff --git a/tests/unit/string_view_test.c b/tests/unit/string_view_test.c
index 1d8627f..fe3dacb 100644
--- a/tests/unit/string_view_test.c
+++ b/tests/unit/string_view_test.c
@@ -48,6 +48,10 @@ string_view_to_u32_test(const MunitParameter params[], void *user_data_or_fixtur
 
     assert_uint32(string_view_to_u32(str), ==, 69);
 
+    str = (string_view_t) { .chars = "39;", .size = 2 };
+
+    assert_uint32(string_view_to_u32(str), ==, 39);
+
     return MUNIT_OK;
 }
 
-- 
2.44.0


^ permalink raw reply	[flat|nested] 31+ messages in thread
* [PATCH olang] string_view: add n + 1 test to string_view_to_u32 function
@ 2024-03-02 19:02 Johnny Richard
  2024-03-02 18:03 ` [olang/patches/.build.yml] build failed builds.sr.ht
  0 siblings, 1 reply; 31+ messages in thread
From: Johnny Richard @ 2024-03-02 19:02 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 tests/unit/string_view_test.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/tests/unit/string_view_test.c b/tests/unit/string_view_test.c
index 1d8627f..fe3dacb 100644
--- a/tests/unit/string_view_test.c
+++ b/tests/unit/string_view_test.c
@@ -48,6 +48,10 @@ string_view_to_u32_test(const MunitParameter params[], void *user_data_or_fixtur
 
     assert_uint32(string_view_to_u32(str), ==, 69);
 
+    str = (string_view_t) { .chars = "39;", .size = 2 };
+
+    assert_uint32(string_view_to_u32(str), ==, 39);
+
     return MUNIT_OK;
 }
 
-- 
2.44.0


^ permalink raw reply	[flat|nested] 31+ messages in thread
* [PATCH olang v2] utils: add arena
@ 2024-02-20 16:39 Carlos Maniero
  2024-02-20 16:45 ` [olang/patches/.build.yml] build failed builds.sr.ht
  0 siblings, 1 reply; 31+ messages in thread
From: Carlos Maniero @ 2024-02-20 16:39 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>
---
v2:
  - fix lint
  - improve commit message

 .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..3d084ab
--- /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 <stdlib.h>
+#include <stdio.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..a3e97aa
--- /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 <stdlib.h>
+#include <stdint.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] 31+ messages in thread
* [PATCH olang] utils: add arena
@ 2024-02-20 16:10 Carlos Maniero
  2024-02-20 16:15 ` [olang/patches/.build.yml] build failed builds.sr.ht
  0 siblings, 1 reply; 31+ messages in thread
From: Carlos Maniero @ 2024-02-20 16:10 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

This is a simple implementation of arena. At this point arena is not
expandable. We can add support to it if neded after we implement linked
lists.

Since arena was the first code unit-tested, I've completed the unit
tests setup at this commit.

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..3d084ab
--- /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 <stdlib.h>
+#include <stdio.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..a3e97aa
--- /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 <stdlib.h>
+#include <stdint.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] 31+ messages in thread
* [PATCH olang v4 4/4] lexer: test: add integration tests for --dump-tokens
@ 2024-02-19 21:04 Johnny Richard
  2024-02-19 20:07 ` [olang/patches/.build.yml] build failed builds.sr.ht
  0 siblings, 1 reply; 31+ messages in thread
From: Johnny Richard @ 2024-02-19 21:04 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero, Johnny Richard

From: Carlos Maniero <carlos@maniero.me>

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   | 14 ++++++++++
 4 files changed, 57 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..1fd70c7 100644
--- a/tests/integration/cli_test.c
+++ b/tests/integration/cli_test.c
@@ -23,6 +23,20 @@ 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");
     return MUNIT_OK;
 }
 
-- 
2.43.2


^ permalink raw reply	[flat|nested] 31+ messages in thread
* [PATCH olang v2 2/2] lexer: create --dump-tokens cli command
@ 2024-02-19  1:23 Johnny Richard
  2024-02-19  0:27 ` [olang/patches/.build.yml] build failed builds.sr.ht
  0 siblings, 1 reply; 31+ messages in thread
From: Johnny Richard @ 2024-02-19  1:23 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 make linter-fix due to linter build failure

 examples/main_exit.0 |   3 +
 src/0c.c             | 121 ++++++++++++++++++++++-
 src/lexer.c          | 224 +++++++++++++++++++++++++++++++++++++++++++
 src/lexer.h          |  74 ++++++++++++++
 4 files changed, 420 insertions(+), 2 deletions(-)
 create mode 100644 examples/main_exit.0
 create mode 100644 src/lexer.c
 create mode 100644 src/lexer.h

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 */
-- 
2.43.2


^ permalink raw reply	[flat|nested] 31+ messages in thread
* [PATCH olang 2/2] lexer: create --dump-tokens cli command
@ 2024-02-19  1:15 Johnny Richard
  2024-02-19  0:20 ` [olang/patches/.build.yml] build failed builds.sr.ht
  0 siblings, 1 reply; 31+ messages in thread
From: Johnny Richard @ 2024-02-19  1:15 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>
---
 examples/main_exit.0 |   3 +
 src/0c.c             | 121 ++++++++++++++++++++++-
 src/lexer.c          | 224 +++++++++++++++++++++++++++++++++++++++++++
 src/lexer.h          |  74 ++++++++++++++
 4 files changed, 420 insertions(+), 2 deletions(-)
 create mode 100644 examples/main_exit.0
 create mode 100644 src/lexer.c
 create mode 100644 src/lexer.h

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..7866a9a
--- /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 */
-- 
2.43.2


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

This reverts commit 0a48a94d9ade6ac92bbc42c3e7681f326d4f8d70.
---
 .gitignore     |  1 -
 docs/Makefile  | 20 --------------------
 docs/conf.py   | 28 ----------------------------
 docs/index.rst | 13 -------------
 4 files changed, 62 deletions(-)
 delete mode 100644 docs/Makefile
 delete mode 100644 docs/conf.py
 delete mode 100644 docs/index.rst

diff --git a/.gitignore b/.gitignore
index 77f3000..b140d08 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,3 @@
 0c
 build
 *.o
-docs/_build
diff --git a/docs/Makefile b/docs/Makefile
deleted file mode 100644
index d4bb2cb..0000000
--- a/docs/Makefile
+++ /dev/null
@@ -1,20 +0,0 @@
-# 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
deleted file mode 100644
index ee7421a..0000000
--- a/docs/conf.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# 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
deleted file mode 100644
index 4ad0971..0000000
--- a/docs/index.rst
+++ /dev/null
@@ -1,13 +0,0 @@
-Welcome to olang's documentation!
-=================================
-
-.. toctree::
-   :caption: Contents:
-
-
-
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`search`
-- 
2.34.1


^ permalink raw reply	[flat|nested] 31+ messages in thread
* [PATCH olang] docs: add HACKING documentation
@ 2024-02-17  4:23 Carlos Maniero
  2024-02-17  4:23 ` [olang/patches/.build.yml] build failed builds.sr.ht
  0 siblings, 1 reply; 31+ messages in thread
From: Carlos Maniero @ 2024-02-17  4:23 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>
---
 docs/index.rst         |   8 ++-
 docs/pages/hacking.rst | 158 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 164 insertions(+), 2 deletions(-)
 create mode 100644 docs/pages/hacking.rst

diff --git a/docs/index.rst b/docs/index.rst
index 4ad0971..f09a97d 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,9 +1,13 @@
-Welcome to olang's documentation!
-=================================
+Welcome to olang's docs!
+========================
+The zero programming language.
+
 
 .. toctree::
    :caption: Contents:
 
+   pages/hacking
+
 
 
 Indices and tables
diff --git a/docs/pages/hacking.rst b/docs/pages/hacking.rst
new file mode 100644
index 0000000..5fd7a39
--- /dev/null
+++ b/docs/pages/hacking.rst
@@ -0,0 +1,158 @@
+=======
+Hacking
+=======
+
+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.
+
+.. code-block:: 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:
+
+.. code-block:: sh
+
+  make linter
+
+Most of the common code style mistakes are fixed by:
+
+.. code-block:: 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:
+
+.. code-block:: 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
+^^^^^^^^^^^^^^
+
+#. Write single-purpose commits.
+#. Write a meaningful commit message.
+#. 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:
+
+.. code-block:: 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-lette**
+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.
+
+.. code-block:: 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.
+
+.. code-block:: 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!
-- 
2.34.1


^ permalink raw reply	[flat|nested] 31+ messages in thread
* [PATCH olang v2 2/2] tests: add integration test setup
@ 2024-02-16  2:58 Carlos Maniero
  2024-02-16  3:03 ` [olang/patches/.build.yml] build failed builds.sr.ht
  0 siblings, 1 reply; 31+ messages in thread
From: Carlos Maniero @ 2024-02-16  2:58 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..0ca0b37
--- /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;
+
+void
+static 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] 31+ messages in thread
* [PATCH olang] style: fix clang-format format indentation
@ 2024-02-14 23:36 Carlos Maniero
  2024-02-14 23:41 ` [olang/patches/.build.yml] build failed builds.sr.ht
  0 siblings, 1 reply; 31+ messages in thread
From: Carlos Maniero @ 2024-02-14 23:36 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Carlos Maniero

editorconfig was set to 4 spaces clang-format was expecting 2.

Signed-off-by: Carlos Maniero <carlos@maniero.me>
---
 .clang-format | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/.clang-format b/.clang-format
index 2850fff..b76afee 100644
--- a/.clang-format
+++ b/.clang-format
@@ -81,8 +81,8 @@ ColumnLimit:     120
 CommentPragmas:  '^ IWYU pragma:'
 QualifierAlignment: Leave
 CompactNamespaces: false
-ConstructorInitializerIndentWidth: 2
-ContinuationIndentWidth: 2
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
 Cpp11BracedListStyle: false
 DeriveLineEnding: true
 DerivePointerAlignment: false
@@ -124,7 +124,7 @@ IndentGotoLabels: true
 IndentPPDirectives: None
 IndentExternBlock: AfterExternBlock
 IndentRequiresClause: true
-IndentWidth:     2
+IndentWidth:     4
 IndentWrappedFunctionNames: false
 InsertBraces:    false
 InsertTrailingCommas: None
-- 
2.34.1


^ permalink raw reply	[flat|nested] 31+ messages in thread
* [PATCH olang] build: enable continuous integration through .build.yml
@ 2024-02-13 21:36 Johnny Richard
  2024-02-13 20:34 ` [olang/patches/.build.yml] build failed builds.sr.ht
  0 siblings, 1 reply; 31+ messages in thread
From: Johnny Richard @ 2024-02-13 21:36 UTC (permalink / raw)
  To: ~johnnyrichard/olang-devel; +Cc: Johnny Richard

A small build pipeline with linter and compilation settings.

Signed-off-by: Johnny Richard <johnny@johnnyrichard.com>
---
 .build.yml | 12 ++++++++++++
 1 file changed, 12 insertions(+)
 create mode 100644 .build.yml

diff --git a/.build.yml b/.build.yml
new file mode 100644
index 0000000..35a5da8
--- /dev/null
+++ b/.build.yml
@@ -0,0 +1,12 @@
+image: archlinux
+packages:
+  - gcc
+  - make
+  - clang
+tasks:
+  - lint: |
+      cd olang
+      make linter
+  - build: |
+      cd olang
+      make
-- 
2.42.0


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

end of thread, other threads:[~2024-08-13 18:09 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-03-25 22:36 [PATCH olang v1 0/2] Introduce CLI option for improved AST tree visualization Johnny Richard
2024-03-25 22:36 ` [PATCH olang v1 1/2] cli: add new option to pretty print AST tree Johnny Richard
2024-03-25 22:36 ` [PATCH olang v1 2/2] cli: remove code duplication Johnny Richard
2024-03-25 21:37   ` [olang/patches/.build.yml] build failed builds.sr.ht
2024-03-26  2:32     ` Carlos Maniero
2024-03-26  2:35       ` Carlos Maniero
2024-03-26  2:30   ` [PATCH olang v1 2/2] cli: remove code duplication Carlos Maniero
  -- strict thread matches above, loose matches on Subject: below --
2024-08-13 18:16 [PATCH olang v1 2/2] ast: inline ast_node_data_t union definition Johnny Richard
2024-08-13 17:27 ` [olang/patches/.build.yml] build failed builds.sr.ht
2024-08-13 19:03   ` Johnny Richard
2024-04-20 13:54 [PATCH olang v1] build: rename linter to format to avoid confusion Johnny Richard
2024-04-20 12:57 ` [olang/patches/.build.yml] build failed builds.sr.ht
2024-03-28 15:58 [PATCH olang v1] fe: lexer: add single line comments support Johnny Richard
2024-03-28 14:59 ` [olang/patches/.build.yml] build failed builds.sr.ht
2024-03-28 16:46   ` Johnny Richard
2024-03-27  3:21 [PATCH olang v1 2/2] docs: spec: add variables and constants specification Carlos Maniero
2024-03-27  3:22 ` [olang/patches/.build.yml] build failed builds.sr.ht
2024-03-13 12:32 [PATCH olang v3] refactor: rename zero programming language to olang Fabio Maciel
2024-03-13 12:33 ` [olang/patches/.build.yml] build failed builds.sr.ht
2024-03-08 20:52 [PATCH olang] parser: abort when parser identifies a syntax error Johnny Richard
2024-03-08 19:54 ` [olang/patches/.build.yml] build failed builds.sr.ht
2024-03-05  8:44 [PATCH olang v2 3/3] cli: add compilation -o option with --save-temps Johnny Richard
2024-03-05  7:51 ` [olang/patches/.build.yml] build failed builds.sr.ht
2024-03-04 19:23 [PATCH olang v1 3/3] cli: add compilation -o option with --save-temps Johnny Richard
2024-03-04 18:33 ` [olang/patches/.build.yml] build failed builds.sr.ht
2024-03-04 19:39   ` Johnny Richard
2024-03-05  2:05     ` Carlos Maniero
2024-03-02 20:01 [PATCH olang] string_view: fix stack buffer overflow on to_u32 Johnny Richard
2024-03-02 19:02 ` [olang/patches/.build.yml] build failed builds.sr.ht
2024-03-02 19:02 [PATCH olang] string_view: add n + 1 test to string_view_to_u32 function Johnny Richard
2024-03-02 18:03 ` [olang/patches/.build.yml] build failed builds.sr.ht
2024-02-20 16:39 [PATCH olang v2] utils: add arena Carlos Maniero
2024-02-20 16:45 ` [olang/patches/.build.yml] build failed builds.sr.ht
2024-02-20 16:10 [PATCH olang] utils: add arena Carlos Maniero
2024-02-20 16:15 ` [olang/patches/.build.yml] build failed builds.sr.ht
2024-02-19 21:04 [PATCH olang v4 4/4] lexer: test: add integration tests for --dump-tokens Johnny Richard
2024-02-19 20:07 ` [olang/patches/.build.yml] build failed builds.sr.ht
2024-02-19  1:23 [PATCH olang v2 2/2] lexer: create --dump-tokens cli command Johnny Richard
2024-02-19  0:27 ` [olang/patches/.build.yml] build failed builds.sr.ht
2024-02-19  1:15 [PATCH olang 2/2] lexer: create --dump-tokens cli command Johnny Richard
2024-02-19  0:20 ` [olang/patches/.build.yml] build failed builds.sr.ht
2024-02-17 13:46 [PATCH olang 2/2] Revert "docs: add sphinx documentation support" Carlos Maniero
2024-02-17 13:51 ` [olang/patches/.build.yml] build failed builds.sr.ht
2024-02-17  4:23 [PATCH olang] docs: add HACKING documentation Carlos Maniero
2024-02-17  4:23 ` [olang/patches/.build.yml] build failed builds.sr.ht
2024-02-16  2:58 [PATCH olang v2 2/2] tests: add integration test setup Carlos Maniero
2024-02-16  3:03 ` [olang/patches/.build.yml] build failed builds.sr.ht
2024-02-14 23:36 [PATCH olang] style: fix clang-format format indentation Carlos Maniero
2024-02-14 23:41 ` [olang/patches/.build.yml] build failed builds.sr.ht
2024-02-13 21:36 [PATCH olang] build: enable continuous integration through .build.yml Johnny Richard
2024-02-13 20:34 ` [olang/patches/.build.yml] build failed 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