From 4b077ac12eaf83c7f910f1a40932670f8161a3e0 Mon Sep 17 00:00:00 2001 From: NICUP14 Date: Tue, 1 Oct 2024 16:34:20 +0200 Subject: [PATCH] Added implicit destruct to assignment; Updated README. --- README.md | 13 +-- include/stdlib/builtin/alloc.ml | 2 + include/stdlib/c/cstdarg.ml | 12 +-- include/stdlib/str_build.ml | 66 +++++++++++++++ include/stdlib/str_list.ml | 38 ++++++++- include/stdlib/string.ml | 6 +- mlpx | 4 +- samples/printf/src/printf.ml | 2 +- skel/include/mlalloc.h | 1 + src/Def.py | 18 +++++ src/Parser.py | 65 +++++++++++---- src/backend/ml/MLWalker.py | 1 + tests/str-buildtest/main.c | 68 ++++++++++++++++ tests/str-buildtest/mlpx | 2 + tests/str-buildtest/src/main.ml | 19 +++++ tests/str-buildtest/src/str_build.ml | 66 +++++++++++++++ tests/string-test/sds | 1 - tests/string-test/src/for.ml | 59 -------------- tests/string-test/src/main.ml | 115 ++++++++++++++++----------- tests/test/src/main.ml | 36 ++++----- 20 files changed, 432 insertions(+), 162 deletions(-) create mode 100644 include/stdlib/str_build.ml create mode 100644 tests/str-buildtest/main.c create mode 100644 tests/str-buildtest/mlpx create mode 100644 tests/str-buildtest/src/main.ml create mode 100644 tests/str-buildtest/src/str_build.ml delete mode 160000 tests/string-test/sds delete mode 100644 tests/string-test/src/for.ml diff --git a/README.md b/README.md index c2053ff..9d8488d 100644 --- a/README.md +++ b/README.md @@ -92,15 +92,16 @@ python mlpx -C tests/test build and run ### Creating projects > [!IMPORTANT] -> The ML project creator utility (`mlpx init`) will be available soon... +> It's recommended to use the `mlpx` build tool as it's specifically designed for this purpose: no libary redundancy and no configuration compared to using the `make` build tool. + +Creating a `MiniLang` project is easy and straight-forward using the `mlpx` utility, which provides two ways with differing build tools. ```txt -# I. Copy project skeleton -cp -r skel +# Using the mlpx build tool +python mlpx init my_new_project -# If using the GNU make build tool instead of mlpx -# II. Configure makefile parameters (ML, MLLIB) -$EDITOR /Makefile +# Using the make build tool +python mlpx makefile-init my_new_project ``` ### Build tools diff --git a/include/stdlib/builtin/alloc.ml b/include/stdlib/builtin/alloc.ml index 8492dba..e0517e8 100644 --- a/include/stdlib/builtin/alloc.ml +++ b/include/stdlib/builtin/alloc.ml @@ -23,6 +23,7 @@ macro alloc_stop literal("#define s_malloc malloc") literal("#define s_realloc realloc") literal("#define s_free free") + literal("#include ") gc_stop(&ml_gc) _gc_running = false @@ -39,6 +40,7 @@ macro alloc_start(_lit) literal("#define s_malloc ml_malloc") literal("#define s_realloc ml_realloc") literal("#define s_free ml_free") + literal("#include ") gc_start(&ml_gc, &_lit) _gc_running = true diff --git a/include/stdlib/c/cstdarg.ml b/include/stdlib/c/cstdarg.ml index b0d37f7..e882de4 100644 --- a/include/stdlib/c/cstdarg.ml +++ b/include/stdlib/c/cstdarg.ml @@ -11,17 +11,17 @@ fun destruct(arg: va_list&) va_end(move(arg)) end -macro va_start(list, param) +macro va_start(_list, _param) # Hacky fix, literal(param) should be void - c_va_start(move(list), cast("int64", literal(param))) + c_va_start(move(_list), cast("int64", literal(_param))) end -macro va_arg_voidptr(list) - c_va_arg(move(list), literal("void*")) +macro va_arg_voidptr(_list) + c_va_arg(move(_list), literal("void*")) end -macro va_arg_int64(list) - cast("int64", c_va_arg(move(list), literal("long long"))) +macro va_arg_int64(_list) + cast("int64", c_va_arg(move(_list), literal("long long"))) end fun va_arg(list: va_list, argx: void*): void* diff --git a/include/stdlib/str_build.ml b/include/stdlib/str_build.ml new file mode 100644 index 0000000..05ef157 --- /dev/null +++ b/include/stdlib/str_build.ml @@ -0,0 +1,66 @@ +import stdlib.c.cstdlib +import stdlib.string + +struct str_build + str_build_len: int64 + str_build_size: int64 + str_build_cs: c_str +end + +fun str_build(size: int64) + let cs = alloc_size(size, true) + ret str_build(0, size, cs) +end + +fun len(sb: str_build&) + ret sb.str_build_len +end + +fun capacity(sb: str_build&) + ret sb.str_build_size +end + +fun to_str(sb: str_build&) + ret str(sb.str_build_cs) +end + +fun to_cstr(sb: str_build&) + let dupl = alloc_size(sb.len) + let src = sb.str_build_cs + memcpy(src, dupl, sb.str_build_len) + + ret dupl +end + +fun append(sb: str_build&, ch: int8) + if sb.len == sb.capacity + panicf("Attempt to append character %c to max-capacity str_build\n", ch) + end + + sb.str_build_cs[sb.str_build_len] = ch + sb.str_build_len = sb.str_build_len + 1 + + ret sb +end + +fun append(sb: str_build&, cs: int8*) + let cs_len = strlen(cs) + if sb.len + cs_len == sb.capacity + panicf("Attempt to append c string '%s' to max-capacity str_build\n", cs) + end + + for it in range(cs_len) + sb.append(cs[it]) + end + + ret sb +end + +fun append(sb: str_build&, s: str&) + let s_len = s.len + if sb.len + s_len == sb.capacity + panicf("Attempt to append string '%s' to max-capacity str_build\n", c_str(s)) + end + + ret sb.append(c_str(s)) +end \ No newline at end of file diff --git a/include/stdlib/str_list.ml b/include/stdlib/str_list.ml index ef6b4be..1e22952 100644 --- a/include/stdlib/str_list.ml +++ b/include/stdlib/str_list.ml @@ -1,15 +1,20 @@ +import stdlib.macro +import stdlib.debug import stdlib.string import stdlib.c.cstdarg macro args_to_str(_arg) to_str(_arg) end + +# Converts each argument of he list to string macro args_to_str(_arg, _other) to_str(_arg), args_to_str(_other) end -# Stores information retuned by "str_list_start" +# Stores an array of strings struct str_list + str_list_size: int64 str_list_cnt: int64 str_list_arr: str* end @@ -24,7 +29,7 @@ fun _str_listv(cnt: int64, listx: va_list) arr at it = str(arg) end - ret str_list(cnt, arr) + ret str_list(cnt, cnt, arr) end fun _str_list(cnt: int64, ...) @@ -39,7 +44,34 @@ fun str_list(cnt: int64, listx: va_list) end macro str_list_from(args) - _str_list(count(args), args_to_str(args)) + _str_list(count(args), count(args), args_to_str(args)) +end + +fun len(list: str_list&) + ret list.str_list_cnt +end + +fun capacity(list: str_list&) + ret list.str_list_size +end + +fun append(list: str_list&, arg: c_str) + if list.len == list.capacity + panicf("Attempt to append c string '%s' to max-capacity str_list\n", arg) + end + + list.str_list_arr[list.str_list_cnt] = arg.str + list.str_list_cnt.incr + + ret list +end + +fun append(list: str_list&, arg: str&) + if list.len == list.capacity + panicf("Attempt to append string '%s' to max-capacity str_build\n", c_str(arg)) + end + + ret list.append(c_str(arg)) end # For-loop support diff --git a/include/stdlib/string.ml b/include/stdlib/string.ml index 71e34fb..f512ff1 100644 --- a/include/stdlib/string.ml +++ b/include/stdlib/string.ml @@ -245,11 +245,13 @@ end # Create an sds string from a boolean value. fun to_str(value: bool): str + let cs = null if value - ret str("true") + cs = "true" else - ret str("false") + cs = "false" end + ret str(cs) end # Create an sds string from a long long value. diff --git a/mlpx b/mlpx index 87d5cc5..e972d46 100644 --- a/mlpx +++ b/mlpx @@ -259,7 +259,9 @@ def handle_cmd_list(args: List[str]): if len(parts) > 0: handle_cmd(parts) else: - print_error(f'Missing command in list {" ".join(args)}') + # Defaults to build and run for convenience + handle_build([]) + handle_run([]) if __name__ == '__main__': diff --git a/samples/printf/src/printf.ml b/samples/printf/src/printf.ml index c554736..c26dfd8 100644 --- a/samples/printf/src/printf.ml +++ b/samples/printf/src/printf.ml @@ -81,7 +81,7 @@ fun custom_printf(format: int8*, ...): void let width = 0 if format_ch == '*' width = va_arg_int64(va_list) - else: + else width = 0 for format_ch in format_range if isdigit(format_ch) == 0 diff --git a/skel/include/mlalloc.h b/skel/include/mlalloc.h index 86f6377..f031c95 100644 --- a/skel/include/mlalloc.h +++ b/skel/include/mlalloc.h @@ -4,6 +4,7 @@ #ifndef ML_ALLOC_H #define ML_ALLOC_H +#define ML_ALLOC_GC #ifndef ML_ALLOC_GC GarbageCollector ml_gc; diff --git a/src/Def.py b/src/Def.py index 6fdf79f..d7b1b3f 100644 --- a/src/Def.py +++ b/src/Def.py @@ -44,6 +44,15 @@ class Register(enum.Enum): id_max = 14 +# Context is provided as an extension due to the maturity of the project +# Scope: context_map.get(full_name).scope +# Small name: context_map.get(full_name).name +@dataclass +class Context: + name: str + scope: str + + class VariableMetaKind(enum.Enum): """ Defines all possible structural types. @@ -479,6 +488,14 @@ def global_modf_of(kind: VariableKind) -> str: return modf_map[kind] +def curr_scope() -> str: + return module_name_list + fun_name_list + +def name_of(full_name: str) -> str: + if full_name not in context_map: + print_error('name_of', f'No such small name for {full_name}') + + return context_map.get(full_name).name def full_name_of_fun(name: str, force_global: bool = False, exhaustive_match: bool = True): # Namespace match @@ -1498,6 +1515,7 @@ def get_counter(): counter = 0 var_off = 0 block_cnt = 0 +context_map: Dict[str, Context] = dict() macro_map: Dict[str, Macro] = dict() var_map: Dict[str, Variable] = dict() fun_map: Dict[str, Function] = dict() diff --git a/src/Parser.py b/src/Parser.py index c08bb08..67b7a99 100644 --- a/src/Parser.py +++ b/src/Parser.py @@ -21,6 +21,7 @@ from Def import Node from Def import NodeKind from Def import Variable +from Def import Context from Def import VariableCompKind from Def import VariableType from Def import VariableMetaKind @@ -58,6 +59,7 @@ from Def import type_of_ident from Def import type_of_lit from Def import type_compatible +from Def import curr_scope from Def import full_name_of_var from Def import full_name_of_fun from Def import needs_widen @@ -343,18 +345,25 @@ def cmp_precedence(t: Token, t2: Token): op_stack.append(token) continue - # Handles Unary operator (token correction) + # Handles unary operators (token correction) op_token = token if prev_token is None or token_is_op(prev_token.kind) or prev_token.kind == TokenKind.LPAREN: - if token.kind == TokenKind.NOT: - op_token = token - elif token.kind == TokenKind.MULT: - op_token = Token(TokenKind.DEREF, '*') - elif token.kind == TokenKind.BIT_AND: - op_token = Token(TokenKind.AMP, '&') + # Fix for unary plus/minus + if token.kind == TokenKind.PLUS: + continue + elif token.kind == TokenKind.MINUS: + postfix_tokens.append(Token(TokenKind.INT_LIT, '0')) + else: - print_error('to_postfix', - f'Invalid unary operator kind {token.kind}', self) + if token.kind == TokenKind.NOT: + op_token = token + elif token.kind == TokenKind.MULT: + op_token = Token(TokenKind.DEREF, '*') + elif token.kind == TokenKind.BIT_AND: + op_token = Token(TokenKind.AMP, '&') + else: + print_error('to_postfix', + f'Invalid unary operator kind {token.kind}', self) while len(op_stack) > 0 and (not token_is_rassoc(op_stack[-1].kind)) and op_stack[-1].kind != TokenKind.LPAREN and cmp_precedence(op_token, op_stack[-1]): postfix_tokens.append(op_stack.pop()) @@ -780,8 +789,8 @@ def ref(self, node: Node) -> tuple[Node, Node]: if node.value not in Def.fun_map: return (None, ref_node(node)) - tmp_name = full_name_of_var( - f'{node.value}_tmp_ref_{get_counter()}', force_local=True) + small_name = f'{node.value}_tmp_ref_{get_counter()}' + tmp_name = full_name_of_var(small_name, force_local=True) def get_type(node: Node): return node.ntype @@ -793,6 +802,8 @@ def get_type(node: Node): print_error('ref', f'No signature of {fun.name} matches {list(map(Def.rev_type_of, map(get_type, args_to_list(node.left))))} out of {[list(map(Def.rev_type_of, sig.arg_types)) for sig in fun.signatures]}') + Def.context_map[tmp_name] = Context(small_name, curr_scope()) + Def.ident_map[tmp_name] = sig.ret_type.meta_kind() tmp_decl = self.declare( tmp_name, sig.ret_type, sig.ret_type.elem_ckind, init_node=node) @@ -1067,6 +1078,20 @@ def to_tree(self, tokens: list[Token]) -> Node: print_error('to_tree', f'to_tree: Incompatible types {kind} {rev_type_of(left.ntype)}, {rev_type_of(right.ntype)} (check #2) ({kind} {left.value} {right.value})', self) + # Destructs old value on assignment + if kind == NodeKind.OP_ASSIGN and left.ntype.ckind == struct_ckind: + destr_fun = Def.fun_map.get('destruct') + destr_sig = _find_signature( + destr_fun, [ref_of(left.ntype)]) + + if not destr_fun or not destr_sig: + print_warning( + 'to_tree', f'No destructor implemented for {left.ntype.name}; Resource is not properly cleaned up', parser=self) + + else: + to_predeferred( + Node(NodeKind.FUN_CALL, destr_sig.ret_type, 'destruct', ref_node(left))) + # Disallows string literal modification if kind == NodeKind.OP_ASSIGN and left.kind == NodeKind.ARR_ACC and left.left.kind == NodeKind.STR_LIT: print_error('to_tree', @@ -1357,8 +1382,10 @@ def namespace_statement(self) -> Optional[Node]: namespace = self.match_token(TokenKind.IDENT).value self.next_line() - Def.ident_map[full_name_of_fun( - namespace, force_global=True)] = VariableMetaKind.NAMESPACE + full_name = full_name_of_fun( + namespace, force_global=True) + Def.context_map[full_name] = Context(namespace, curr_scope()) + Def.ident_map[full_name] = VariableMetaKind.NAMESPACE Def.module_name_list.append(namespace) namespace_node = Node(NodeKind.NAMESPACE, void_type, namespace, self.compound_statement()) @@ -1466,6 +1493,7 @@ def for_statement(self, add_predef: bool) -> Optional[Node]: self.check_ident(name, vtype.meta_kind(), use_mkind=True) meta_kind = vtype.meta_kind() + Def.context_map[name] = Context(name, curr_scope()) Def.ident_map[name] = meta_kind if meta_kind == VariableMetaKind.STRUCT: def add_prefix(elem_name: str, name: str = name) -> str: @@ -1664,8 +1692,10 @@ def ret_statement(self) -> Optional[Node]: Def.returned.append(node.value) # Stores the return in a temp (store -> destr -> ret) + small_name = f'ret_{self.lineno}' full_name = full_name_of_var( - f'ret_{self.lineno}', force_local=True) + small_name, force_local=True) + Def.context_map[full_name] = Context(small_name, curr_scope()) Def.ident_map[full_name] = node.ntype.meta_kind() Def.returned.append(full_name) @@ -1924,6 +1954,7 @@ def fun_declaration(self, is_extern: bool = False, in_generic: bool = False, is_ fun = Function(full_name, len(arg_types), full_arg_names, arg_types, ret_type, 0, is_variadic, is_extern, [signature]) + Def.context_map[full_name] = Context(name, curr_scope()) Def.ident_map[full_name] = VariableMetaKind.FUN Def.fun_map[full_name] = fun @@ -1948,6 +1979,7 @@ def fun_declaration(self, is_extern: bool = False, in_generic: bool = False, is_ arg_name, force_local=True, exhaustive_match=False) self.check_ident(full_arg_name, meta_kind, use_mkind=True) + Def.context_map[full_arg_name] = Context(arg_name, curr_scope()) Def.ident_map[full_arg_name] = meta_kind Def.var_off += size_of(arg_type.ckind) @@ -2076,6 +2108,7 @@ def struct_declaration(self, is_extern: bool = False) -> Optional[Node]: struct.elem_names), full_arg_names, struct.elem_types, struct.vtype, is_extern, False, None) Def.fun_sig_map[sig_name] = fun_name + Def.context_map[fun_name] = Context(fun_name, curr_scope()) Def.ident_map[fun_name] = VariableMetaKind.FUN Def.fun_map[fun_name] = Function(fun_name, len(struct.elem_names), struct.elem_names, struct.elem_types, struct.vtype, 0, False, False, [signature]) @@ -2098,6 +2131,8 @@ def inject_decl(tpl: Tuple[str, str, VariableType]) -> Node: return Node(NodeKind.OP_ASSIGN, var_type, '=', elem_acc, node) + Def.context_map[tmp_name] = Context(tmp_name, curr_scope()) + Def.ident_map[tmp_name] = struct.vtype.meta_kind() decl = Node(NodeKind.STRUCT_DECL, struct.vtype, tmp_name) ret = Node(NodeKind.RET, struct.vtype, '', Node( NodeKind.IDENT, struct.vtype, tmp_name)) @@ -2334,6 +2369,7 @@ def heredoc_declaration(self): def struct_elem_declaration(self, names: List[str], og_struct: Structure) -> Optional[Node]: for new_name, og_name, var_type in zip(names, og_struct.elem_names, og_struct.elem_types): meta_kind = var_type.meta_kind() + Def.context_map[new_name] = Context(new_name, curr_scope()) Def.ident_map[new_name] = meta_kind if meta_kind == VariableMetaKind.STRUCT: @@ -2568,6 +2604,7 @@ def declaration(self, is_struct: bool = False) -> Optional[Node]: Def.struct_map[Def.struct_name].elem_types.append(var_type) self.check_ident(full_name, use_mkind=True) + Def.context_map[full_name] = Context(name, curr_scope()) Def.ident_map[full_name] = meta_kind node = self.declare(full_name, var_type, elem_ckind, diff --git a/src/backend/ml/MLWalker.py b/src/backend/ml/MLWalker.py index 750588b..13e063c 100644 --- a/src/backend/ml/MLWalker.py +++ b/src/backend/ml/MLWalker.py @@ -39,6 +39,7 @@ def ml_walker_step(node: Node, parent: Node, left, right, middle, indent_cnt: in return f'(*{node.value})' return node.value + if node.kind == NodeKind.OP_ADD: return f'({left} + {right})' if node.kind == NodeKind.OP_SUB: diff --git a/tests/str-buildtest/main.c b/tests/str-buildtest/main.c new file mode 100644 index 0000000..dc0b72d --- /dev/null +++ b/tests/str-buildtest/main.c @@ -0,0 +1,68 @@ +int dbg.main (int esi, int edx) { + loc_0x407750: + // CALL XREF from sym.__tmainCRTStartup @ 0x4013c2(x) + push (rsi) // int32_t main()// + push (rbx) + rsp -= 0x68 + sym.__main () + rbx = var_40h + edx = 1 // int64_t arg2 + ecx = 0x1e // 30 // int64_t arg1 + sym.alloc_size_int64_bool_2 () // sym.alloc_size_int64_bool_2(0x0, 0x0) + rsi = var_20h + rdx = rbx // int64_t arg2 + r8d = 0x63 // 'c' // 99 // int64_t arg_28h + rcx = rsi // int64_t arg1 + qword [var_50h] = rax + qword [var_40h] = 0 + qword [var_48h] = 0x1e // [0x1e:8]=-1 // 30 + sym.append_str_buildref_int8_2 () // sym.append_str_buildref_int8_2(0x0, 0x177f98, 0x177fc0, 0x177f98) + rdx = rbx // int64_t arg2 + rcx = rsi // int64_t arg1 + r8d = 0x6f // 'o' // 111 // int64_t arg_28h + sym.append_str_buildref_int8_2 () // sym.append_str_buildref_int8_2(0x0, 0x177f98, 0x177fc0, 0x177f98) + rdx = rbx // int64_t arg2 + rcx = rsi // int64_t arg1 + r8d = 0x70 // 'p' // 112 // int64_t arg_28h + sym.append_str_buildref_int8_2 () // sym.append_str_buildref_int8_2(0x0, 0x177f98, 0x177fc0, 0x177f98) + rdx = rbx // int64_t arg2 + rcx = rsi // int64_t arg1 + r8d = 0x79 // 'y' // 121 // int64_t arg_28h + sym.append_str_buildref_int8_2 () // sym.append_str_buildref_int8_2(0x0, 0x177f98, 0x177fc0, 0x177f98) + rdx = rbx // int64_t arg2 + rcx = rsi // int64_t arg1 + r8 = rip + str._this_one_also // 0x409966 // " this one also" // int64_t arg3 + sym.append_str_buildref_int8ptr_2 () // sym.append_str_buildref_int8ptr_2(0x0, 0x177f98, 0x177fc0, 0x177f98, 0x409966) + rcx = rbx // int64_t arg1 + dbg.to_str_str_buildref_1 () // dbg.to_str_str_buildref_1(0x0) + rsi = qword [0x004080d0] // [0x4080d0:8]=0x407610 sym.__acrt_iob_func // sym.__acrt_iob_func + ecx = 1 + rbx = rax + rsi () // sym.__acrt_iob_func // sym.__acrt_iob_func(0x0) + r8d = 8 // size_t nitems + edx = 1 // size_t size + rcx = rip + str.Result:_ // 0x409975 // "Result: " // const void *ptr + r9 = rax // FILE *stream + sym.fwrite () + // size_t fwrite(0x203a746c75736552, -1, -1, ?) + rcx = rbx // int64_t arg1 + dbg.sdsdup () // dbg.sdsdup(0x0) + ecx = 1 + rbx = rax + rsi () // sym.__acrt_iob_func // sym.__acrt_iob_func(0x0) + rcx = rbx // const char *s + rdx = rax // FILE *stream + sym.fputs () + // int fputs(-1, ?) + rcx = rbx // int64_t arg1 + dbg.sdsfree () // dbg.sdsfree(0x0) + rcx = rip + 0x213e // 0x40997e // const char *s + sym.puts () + // int puts("") + eax = 0 + rsp += 0x68 + rbx = pop () + rsi = pop () + re + // (break) +} diff --git a/tests/str-buildtest/mlpx b/tests/str-buildtest/mlpx new file mode 100644 index 0000000..0f06c32 --- /dev/null +++ b/tests/str-buildtest/mlpx @@ -0,0 +1,2 @@ +#!/bin/bash +../../mlpx -p ../.. $@ diff --git a/tests/str-buildtest/src/main.ml b/tests/str-buildtest/src/main.ml new file mode 100644 index 0000000..4e1c121 --- /dev/null +++ b/tests/str-buildtest/src/main.ml @@ -0,0 +1,19 @@ +import stdlib.io.print +import src.str_build + +fun main: int32 + # Starts the garbage collector + let bos = 0 + alloc_start(bos) + + let sb = str_build(30) + sb.append('c') + sb.append('o') + sb.append('p') + sb.append('y') + sb.append(" this one also") + + println("Result: ", sb.to_str) + + ret 0 +end \ No newline at end of file diff --git a/tests/str-buildtest/src/str_build.ml b/tests/str-buildtest/src/str_build.ml new file mode 100644 index 0000000..05ef157 --- /dev/null +++ b/tests/str-buildtest/src/str_build.ml @@ -0,0 +1,66 @@ +import stdlib.c.cstdlib +import stdlib.string + +struct str_build + str_build_len: int64 + str_build_size: int64 + str_build_cs: c_str +end + +fun str_build(size: int64) + let cs = alloc_size(size, true) + ret str_build(0, size, cs) +end + +fun len(sb: str_build&) + ret sb.str_build_len +end + +fun capacity(sb: str_build&) + ret sb.str_build_size +end + +fun to_str(sb: str_build&) + ret str(sb.str_build_cs) +end + +fun to_cstr(sb: str_build&) + let dupl = alloc_size(sb.len) + let src = sb.str_build_cs + memcpy(src, dupl, sb.str_build_len) + + ret dupl +end + +fun append(sb: str_build&, ch: int8) + if sb.len == sb.capacity + panicf("Attempt to append character %c to max-capacity str_build\n", ch) + end + + sb.str_build_cs[sb.str_build_len] = ch + sb.str_build_len = sb.str_build_len + 1 + + ret sb +end + +fun append(sb: str_build&, cs: int8*) + let cs_len = strlen(cs) + if sb.len + cs_len == sb.capacity + panicf("Attempt to append c string '%s' to max-capacity str_build\n", cs) + end + + for it in range(cs_len) + sb.append(cs[it]) + end + + ret sb +end + +fun append(sb: str_build&, s: str&) + let s_len = s.len + if sb.len + s_len == sb.capacity + panicf("Attempt to append string '%s' to max-capacity str_build\n", c_str(s)) + end + + ret sb.append(c_str(s)) +end \ No newline at end of file diff --git a/tests/string-test/sds b/tests/string-test/sds deleted file mode 160000 index a9a03bb..0000000 --- a/tests/string-test/sds +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a9a03bb3304030bb8a93823a9aeb03c157831ba9 diff --git a/tests/string-test/src/for.ml b/tests/string-test/src/for.ml deleted file mode 100644 index 47186a1..0000000 --- a/tests/string-test/src/for.ml +++ /dev/null @@ -1,59 +0,0 @@ -import stdlib.c.cdef -import stdlib.c.cstdlib - -# Integer range -struct range - range_idx: int64 - range_start: int64 - range_stop: int64 -end - -fun range(range_start: int64, range_stop: int64): range - ret range(0, range_start, range_stop) -end - -fun range(range_stop: int64): range - ret range(0, 0, range_stop) -end - -fun iter(arg: range): range - ret arg -end - -fun start(arg: range&): int64 - ret arg.range_start -end - -fun stop(arg: range&): bool - ret arg.range_idx < arg.range_stop -end - -fun next(arg: range&): int64 - arg.range_idx = arg.range_idx + 1 - ret arg.range_idx -end - -# String range -struct c_str_range - c_str_range_str: c_str - c_str_range_idx: int64 - c_str_range_start: int64 - c_str_range_stop: int64 -end - -fun iter(arg: c_str): c_str_range - ret c_str_range(arg, 0, 0, strlen(arg)) -end - -fun start(arg: c_str_range&): c_char - ret (arg.c_str_range_str) at (arg.c_str_range_start) -end - -fun stop(arg: c_str_range&): bool - ret arg.c_str_range_idx < arg.c_str_range_stop -end - -fun next(arg: c_str_range&): c_char - arg.c_str_range_idx = arg.c_str_range_idx + 1 - ret arg.c_str_range_str at arg.c_str_range_idx -end \ No newline at end of file diff --git a/tests/string-test/src/main.ml b/tests/string-test/src/main.ml index c6ed06b..949cd68 100644 --- a/tests/string-test/src/main.ml +++ b/tests/string-test/src/main.ml @@ -1,74 +1,93 @@ +import stdlib.macro import stdlib.string -import stdlib.io.file +import stdlib.str_list +import stdlib.str_build import stdlib.io.print -import stdlib.convert -import stdlib.c.cstdlib -import src.for -fun part_one(st: c_stream): void - let sum = 0 - let max_sum = 0 - let s: str = extend(empty_str, 256) +fun str(cnt: int64, ch: int8) + let s = empty_str.extend(cnt) + memset(s.c_str, ch, cnt) - while read_line(st, s, 256) - s = s.trim("\n") - - if s.len == 0 - max_sum = sum if sum > max_sum else max_sum - sum = 0 - else - sum = sum + to_int64(c_str(s)) - end - end + ret s +end - "Part one: ".print - max_sum.println +fun concat(s: str&, ch: int8) + ret s.concat(str(1, ch)) end -fun part_two(st: c_stream): void - let sum = 0 - let max_sum = 0 - let max_sum2 = 0 - let max_sum3 = 0 - let s: str = extend(empty_str, 256) +fun _format(cnt: int64, fmt: str&, ...): str + let list: va_list + va_start(list, fmt) + + let idx = 0 + let args_idx = 0 + let skip = false + let pos = false + let args = str_list(cnt, list) - while read_line(st, s, 256) - s = s.trim("\n") + let buf = false + let start_idx = 0 + let end_idx = 0 - if s.len == 0 - if sum > max_sum - max_sum3 = max_sum2 - max_sum2 = max_sum - max_sum = sum + let ch: int8 = 0 + let res = str_build(1000) + while idx < fmt.len && (ch = c_str(fmt)[idx]) > '\0' + if !skip && ch == '\\' + skip = true + elif !skip && ch == '{' + if buf + res.append(fmt.substr(start_idx, end_idx)) + buf = false + end - elif sum > max_sum2 - max_sum3 = max_sum2 - max_sum2 = sum + idx.incr + pos = false + let pos_idx = 0 + while idx < fmt.len && ( + ch = c_str(fmt)[idx]) != '}' && isdigit(ch) > 0 + pos = true + pos_idx = pos_idx * 10 + (ch - '0') + idx.incr + end - elif sum > max_sum3 - max_sum3 = sum + if !pos + pos_idx = args_idx + args_idx.incr end - sum = 0 + if pos_idx > args.str_list_cnt || pos_idx < 0 + panicf("Invalid index: %lld\n", pos_idx) + end + + res.append(args.str_list_arr[pos_idx]) else - sum = sum + to_int64(c_str(s)) + if buf + end_idx = idx + else + start_idx = end_idx = idx + buf = true + end end + + idx.incr end - "Part two: ".print - (max_sum + max_sum2 + max_sum3).println + ret res.to_str end +macro format(_fmt) + _format(1, _fmt) +end + +macro format(_fmt, _other) + _format(count(_other), _fmt, _other) +end fun main: int32 let bos = 0 - gc_start(&ml_gc, &bos) + alloc_start(bos) - let in_file = open_file("input.txt") - part_one(in_file) - rewind(in_file) - part_two(in_file) + print(format("Hello {1} {}".str, "Hello", "Sir")) - gc_stop(&ml_gc) ret 0 end \ No newline at end of file diff --git a/tests/test/src/main.ml b/tests/test/src/main.ml index 7606072..9f576f5 100644 --- a/tests/test/src/main.ml +++ b/tests/test/src/main.ml @@ -1,36 +1,30 @@ import stdlib.io.print -struct mystr - ptr: int8* - length: int64 +struct test + op1: int64 + op2: int64 end -fun mystr(ptr: int8*) - ret mystr(ptr, strlen(ptr)) +fun test(op1: int64) + println("Constructor") + ret test(op1, 0) end -fun copy(arg: mystr&) - ret mystr(arg.ptr) +fun copy(arg: test&) + println("Copy") + ret test(arg.op1, arg.op2) end -fun ret_by_alloc: mystr& - let s: mystr* = null - s.alloc - - *s = mystr("abc") - ret s -end - -fun ret_by_copy - let s = mystr("abc") - ret s +fun destruct(arg: test&) + println("Destruct") end -fun ret_by_move - let s = mystr("abc") - ret move s +fun ret_test + ret test(0) end fun main + let a = ret_test + a = ret_test ret 0 end \ No newline at end of file