Skip to content

Commit

Permalink
Fixed fun rvalue references; Updated README and stdlib documentation.
Browse files Browse the repository at this point in the history
  • Loading branch information
NICUP14 committed Sep 1, 2024
1 parent 383073a commit 635d56d
Show file tree
Hide file tree
Showing 13 changed files with 192 additions and 43 deletions.
55 changes: 54 additions & 1 deletion QUICKSTART.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -287,6 +287,42 @@ for it in range(15)
end
```

### Internal representation

```txt
<iter-ret> <target> = iter(&<target_expr>)
<start-ret> <it> = iter(&<target>)
for(<it> = start(<it>); stop(&<it>) == false; <it> = next(&<it>)) {
<body>
}
```

### 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]
Expand Down Expand Up @@ -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 | `&copy(var)` | `&copy(&var)`
Implicit move | rvalue reference | `var` | `var`
Explicit move | composite | `move(var)` | `var`
Reference | reference | `&var` | `&var`
3 changes: 3 additions & 0 deletions STDLIB.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
3 changes: 2 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
@@ -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)`)
Expand Down
30 changes: 30 additions & 0 deletions docs/stdlib/alloc.md
Original file line number Diff line number Diff line change
@@ -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.
23 changes: 23 additions & 0 deletions docs/stdlib/for.md
Original file line number Diff line number Diff line change
@@ -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.
17 changes: 17 additions & 0 deletions docs/stdlib/macro.md
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion docs/stdlib/read.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Function | Description
-----------|------------
`_read` | `read` handlers that call `fscanf` based on the argument type

## Macro
## Macros

Macro | Description
----------|------------
Expand Down
50 changes: 27 additions & 23 deletions include/stdlib/builtin/alloc.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -87,7 +91,7 @@ macro dealloc(_lit)
else
_lit.free
end
_lit = cast("void*", 0)
_lit = null
end

macro with(_lit, _body)
Expand Down
6 changes: 3 additions & 3 deletions samples/task-mgr/src/task.ml
Original file line number Diff line number Diff line change
Expand Up @@ -142,17 +142,17 @@ 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
elif cmd.equals("list".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)
Expand Down
8 changes: 5 additions & 3 deletions src/Def.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -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}')

Expand Down Expand Up @@ -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=' ')

Expand Down
19 changes: 14 additions & 5 deletions src/Parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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] = []
Expand Down Expand Up @@ -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))
Expand Down
2 changes: 1 addition & 1 deletion src/backend/ml/MLWalker.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
17 changes: 12 additions & 5 deletions tests/test/src/main.ml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 635d56d

Please sign in to comment.