diff --git a/QUICKSTART.md b/QUICKSTART.md index c6860c7..a6c497d 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -273,7 +273,7 @@ end ## For loops -For loops are used to iterate over a sequence using iterators. To extend for loops for custom types, declare the required `start`, `stop` and `next` functions. +For loops are used to iterate over a sequence using iterators. ```txt # For loop syntax @@ -287,6 +287,42 @@ for it in range(15) end ``` +### Internal representation + +```txt + = iter(&) + = iter(&) +for( = start(); stop(&) == false; = next(&)) { + +} +``` + +### Extensibility + +To extend for loops for custom types, declare the required `iter`, `start`, `stop` and `next` methods, operating on references to the given type. + +```txt +# For-loop support for range type + +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 + +``` + ## Import statements > [!TIP] @@ -498,3 +534,20 @@ let mystr = str("Hello") # print(len(concat(mystr, " World!"))) mystr.concat(" World!").len.print ``` + +## RAII + +> [!WARNING] +> In the case where a composite type is missing the `copy` method, the intent defaults to moving (surrendering the ownership) the resource to the function. The compiler warns against these implicit ownership claims. + +RAII (Resource Acquisition Is Initialization) is a memory allocation tehnique where resources like memory, files, or locks are automatically managed by associating their acquisition and release with the lifetime of an object. + +In ML, RAII determines the lifetime of the composite types. Resources are destructed by calling their associated `destruct` and copied by calling `copy`. Both aforementioned methods operate on references of the resources' type. The `move` builtin allows createing rvalue references of resources. + +Description | Type (var) | Notation | Effect +--------------|------------------|--------------|------- +Implicit copy | composite | `var` | `copy(&var)` +Explicit copy | reference | `©(var)` | `©(&var)` +Implicit move | rvalue reference | `var` | `var` +Explicit move | composite | `move(var)` | `var` +Reference | reference | `&var` | `&var` diff --git a/STDLIB.md b/STDLIB.md index 93306bb..0dc8301 100644 --- a/STDLIB.md +++ b/STDLIB.md @@ -16,6 +16,8 @@ The standard library permits the use of both unsafe c standard library functions Module | Parent dir. | Description ----------------------------------|---------------|------------ +[for](docs/stdlib/for.md) | builtin | Convenient for-based constructs and looping utilities +[alloc](docs/stdlib/alloc.md) | builtin | Customizable memory allocation utilities [cdefs](docs/stdlib/cdef.md) | c | Commonly used c type definitions [cstdlib](docs/stdlib/cstdlib.md) | c | Bindings for ported functions of the c standard library [cstarg](docs/stdlib/cstdarg.md) | c | Bindings for the `stdarg.h` c library @@ -25,6 +27,7 @@ Module | Parent dir. | Description [convert](docs/stdlib/convert.md) | - | Type conversion library [debug](docs/stdlib/debug.ml) | - | Customizable rust-like assertables and panic macros [string](docs/stdlib/string.md) | - | Functional-like string library +[macro](docs/stdlib/macro.md) | - | Stand-alone convenience macros backend | alloc | Bindings for the `gc.h` c library (Garbage collector) backend | string | Bindings for the `sds.h` c library (Simple Dynamic Strings) va_utils | legacy | Simplistic `stdarg.h`-like implementation for the assembly backend (deprecated) diff --git a/TODO.md b/TODO.md index 82c4f6a..49b11c2 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,8 @@ # TODO -Solved: 20/31 +Solved: 20/32 +- [ ] Fix defers and variable assignments for control sturctures (`for`, ...) - [X] Fix references causing errors in c backend (`&fun(...)`) - [ ] Suggestive errors for non-existent members - [X] Separate macro (`delimit(" " , args)`) diff --git a/docs/stdlib/alloc.md b/docs/stdlib/alloc.md new file mode 100644 index 0000000..3206a78 --- /dev/null +++ b/docs/stdlib/alloc.md @@ -0,0 +1,30 @@ +# Allocation library + +Source: [include/stdlib/builtin/alloc.ml](../../include/stdlib/builtin/alloc.ml) + +Provides customizable memory allocation utilities. + +## Macros + +Macro | Description +----------------------------|------------ +`alloc_warn` | Default warning for non-gc allocation. +`alloc_stop` | Stops the garbage collector. +`alloc_start(_lit)` | Starts the garbage collector, `_lit` points to the stack bottom. +`alloc(_lit)` | Assigns `_lit` to an allocated memory block based on its size. +`alloc(_lit,_size)` | Assigns `_lit` to an allocated memory block of size `_size`. +`alloc_zeroed(_lit)` | Assigns `_lit` to an zero-filled allocated memory block based on its size. +`alloc_zeroed(_lit, _size)` | Assigns `_lit` to an zero-filled allocated memory block of size `_size`. +`dealloc(_lit)` | Deallocates the memory block pointed by `_lit` and assigns `_lit` to null. +`with(_lit,_body)` | Creates a block in which `_lit` is allocated, used by `_body`, then deallocated. + +## Functions + +Function | Description +-------------|------------ +`alloc_size` | Allocates a memory block of the given size, filled based the `fill` flag. + +## Warnings + +> [!WARNING] +> Memory allocated by the `alloc` utilites use different allocators than the one provided by the c standard library. Thus, calling `free` on a memory address allocated by `alloc` causes undefined behaviour. diff --git a/docs/stdlib/for.md b/docs/stdlib/for.md new file mode 100644 index 0000000..e8d8681 --- /dev/null +++ b/docs/stdlib/for.md @@ -0,0 +1,23 @@ +# For loop utilities + +Source: [include/stdlib/builtin/for.ml](../../include/stdlib/builtin/for.ml) + +Provides convenient for-based constructs and looping utilities. + +## Types + +Types | Description +--------------|------------ +`range` | Type to hold range-based looping information. +`file_range` | Type to hold file-based looping information. +`c_str_range` | Type to hold string-based looping information. + +## Functions + +> [!NOTE] +> `iter`/`start`/`stop`/`next` methods are not discussed in this document. Check the [standard library examples](examples.md) for-based loop examples. + +Functions | Description +------|------------ +range | Creates an iterator to a sequence of numbers based on the `start` and `stop` (optional) values. +lines | Creates an iterator to a sequence of lines from the given file. diff --git a/docs/stdlib/macro.md b/docs/stdlib/macro.md new file mode 100644 index 0000000..13bf4e6 --- /dev/null +++ b/docs/stdlib/macro.md @@ -0,0 +1,17 @@ +# Macro library + +Source: [include/stdlib/macro.ml](../../include/stdlib/macro.ml) + +Provides convenient stand-alone macros. + +## Macro + +Macro | Description +----------|------------ +`repeat` | Repeats the expression list a specified number of times +`delimit` | Inserts a delimiter between each argument of the list +`reverse` | Reverses the order of the arguments of the list +`not` | Negates the given boolean argument +`neg` | Negates the identifier passed as a argument +`incr` | Increments the identifier passed as a argument +`decr` | Decrements the identifier passed as a argument diff --git a/docs/stdlib/read.md b/docs/stdlib/read.md index 7354279..3be13ff 100644 --- a/docs/stdlib/read.md +++ b/docs/stdlib/read.md @@ -10,7 +10,7 @@ Function | Description -----------|------------ `_read` | `read` handlers that call `fscanf` based on the argument type -## Macro +## Macros Macro | Description ----------|------------ diff --git a/include/stdlib/builtin/alloc.ml b/include/stdlib/builtin/alloc.ml index d5ed6d1..7d84bbf 100644 --- a/include/stdlib/builtin/alloc.ml +++ b/include/stdlib/builtin/alloc.ml @@ -45,40 +45,44 @@ macro alloc_start(_lit) defer alloc_stop end -macro alloc(_lit) +fun alloc_size(sz: int64, fill: bool): void* + let ptr = null if _gc_running - _lit = _malloc(size_of(_lit)) + ptr = _malloc(sz) else - _lit = malloc(size_of(_lit)) + ptr = malloc(sz) alloc_warn end - if _lit == null + + if ptr == null panic("Allocation failed.") end + + if fill + memset(ptr, 0, 1) + end + + ret ptr +end + +fun alloc_size(size: int64): void* + ret alloc_size(size, false) +end + +macro alloc(_lit) + _lit = alloc_size(size_of(_lit)) end macro alloc(_lit, _size) - if _gc_running - _lit = _malloc(_size) - else - _lit = malloc(_size) - alloc_warn - end - if _lit == null - panic("Allocation failed.") - end + _lit = alloc_size(_size) end macro alloc_zeroed(_lit) - if _gc_running - _lit = _calloc(1, size_of(_lit)) - else - _lit = calloc(1, size_of(_lit)) - alloc_warn - end - if _lit == null - panic("Allocation failed.") - end + _lit = alloc_size(size_of(_lit), true) +end + +macro alloc_zeroed(_lit, _size) + _lit = alloc_size(_size, true) end macro dealloc(_lit) @@ -87,7 +91,7 @@ macro dealloc(_lit) else _lit.free end - _lit = cast("void*", 0) + _lit = null end macro with(_lit, _body) diff --git a/samples/task-mgr/src/task.ml b/samples/task-mgr/src/task.ml index a25e01b..6d54b06 100644 --- a/samples/task-mgr/src/task.ml +++ b/samples/task-mgr/src/task.ml @@ -142,9 +142,9 @@ fun handle_remove(tsk_list: task_list, cmd: str, args: str) end fun handle_cmd(tsk_list: task_list, full_cmd: str) - let sep = full_cmd.find(" ".str) + let sep = (&full_cmd).find(&(" ".str)) let cmd_idx = sep - 1 if sep != 0 - 1 else 0 - 1 - let cmd: str = substr(full_cmd, 0, cmd_idx) + let cmd: str = substr(&full_cmd, 0, cmd_idx) if cmd.equals("help".str) handle_help @@ -152,7 +152,7 @@ fun handle_cmd(tsk_list: task_list, full_cmd: str) tsk_list.handle_list else assert(sep != 0 - 1) - let args: str = full_cmd.substr(sep + 1, 0 - 1) + let args = substr(&full_cmd, sep + 1, 0 - 1) if cmd.equals("add".str) tsk_list.handle_add(cmd, args) diff --git a/src/Def.py b/src/Def.py index fad0016..e647a1f 100644 --- a/src/Def.py +++ b/src/Def.py @@ -588,7 +588,10 @@ def rev_type_of_ident(name: str) -> str: VariableKind.VOID: 'void', } - def rev_of(ckind: VariableCompKind): + def rev_of(ckind: VariableCompKind, name: str = None): + if name: + return name + if ckind == bool_ckind: return 'bool' @@ -632,7 +635,7 @@ def rev_of(ckind: VariableCompKind): specifier = '*' else: specifier = f'[{ptr.elem_cnt}]*' - return f'{rev_of(ptr.elem_type.ckind)}{specifier}' + return f'{rev_of(ptr.elem_type.ckind, ptr.elem_type.name)}{specifier}' print_error('rev_type_of_ident', f'No such meta kind {meta_kind}') @@ -1234,7 +1237,6 @@ def get_type(node: Node) -> VariableType: args = args_to_list(node) if None in args: - print('DBG: ', fun.name, end='') for arg in args: print(rev_type_of(arg.ntype) if arg else 'None', end=' ') diff --git a/src/Parser.py b/src/Parser.py index a54b905..cc58d73 100644 --- a/src/Parser.py +++ b/src/Parser.py @@ -690,10 +690,20 @@ def ref(self, node: Node) -> tuple[Node, Node]: tmp_name = full_name_of_var( f'{node.value}ref_{self.lineno}', force_local=True) + + def get_type(node: Node): + return node.ntype + + fun = Def.fun_map.get(node.value) + sig = _find_signature( + fun, list(map(get_type, args_to_list(node.left)))) + if not sig: + print_error('ref', '...') + tmp_decl = self.declare( - tmp_name, node.ntype, node.ntype.elem_ckind, init_node=node) + tmp_name, sig.ret_type, sig.ret_type.elem_ckind, init_node=node) - return (tmp_decl, ref_node(Node(NodeKind.IDENT, node.ntype, tmp_name))) + return (tmp_decl, ref_node(Node(NodeKind.IDENT, sig.ret_type, tmp_name))) def to_tree(self, tokens: list[Token]) -> Node: node_stack: list[Node] = [] @@ -823,13 +833,12 @@ def to_tree(self, tokens: list[Token]) -> Node: f'Can only reference identifiers & function return types, got {node.kind}', self) # Rvalue correction for function calls - # if kind == NodeKind.REF: - # print("DBG:", node.kind, node.value) if kind == NodeKind.REF and node.kind == NodeKind.FUN_CALL: tmp_decl, node = self.ref(node) to_predeferred(tmp_decl) - return node + node_stack.append(node) + continue node_stack.append( Node(kind, type_of_op(kind, node.ntype), token.value, node)) diff --git a/src/backend/ml/MLWalker.py b/src/backend/ml/MLWalker.py index edbd4fb..750588b 100644 --- a/src/backend/ml/MLWalker.py +++ b/src/backend/ml/MLWalker.py @@ -108,7 +108,7 @@ def ml_walker_step(node: Node, parent: Node, left, right, middle, indent_cnt: in return f'{color_str(Color.BLUE, "while")} {left}\n{body_indent + right}' if node.kind == NodeKind.FOR: body_indent = indent if node.left.kind != NodeKind.GLUE else '' - return f'{color_str(Color.BLUE, "for")} {ml_walk(parent.left)} in {middle})\n{body_indent + left}' + return f'{color_str(Color.BLUE, "for")} {middle} in {right})\n{body_indent + left}' if node.kind == NodeKind.GLUE: left_indent = indent if parent and parent.kind == NodeKind.GLUE: diff --git a/tests/test/src/main.ml b/tests/test/src/main.ml index 38a8f75..f036b37 100644 --- a/tests/test/src/main.ml +++ b/tests/test/src/main.ml @@ -3,11 +3,18 @@ import stdlib.io.print import stdlib.macro fun main - let s: str& = &empty_str - println(delimit(" ", "Hi", "my", "name", "is", "Nicu")) - for i in range(10) - println i - end + let bos = 0 + alloc_start(bos) + + let arr: int64[5]* = null + arr.alloc + + free(arr) + # let s: str& = &empty_str + # println(delimit(" ", "Hi", "my", "name", "is", "Nicu")) + # for i in range(10) + # println i + # end # for i in range(10) # println i # end