Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,27 @@ python3 benchmark_sort.py
The script generates a random list of 100 numbers and times the sorting process
using 1, 2, 4 and 8 threads. Each run executes roughly 200k iterations of the
engine to ensure the list is fully sorted, and the elapsed time is printed.

### Experimental QuickSort

The repository also includes an experimental quicksort program written in
Fraglets. The rules are defined in `quicksort.fra` and illustrate how to
implement a recursive algorithm using the instruction set. You can run it
similar to the selection sort example by parsing the file and executing the
engine:

```bash
python3 - <<'EOF'
import fraglets
f = fraglets.fraglets()
for line in open('quicksort.fra'):
line = line.strip()
if line and not line.startswith('#'):
f.parse(line)
f.run(20000, 10000, True)
print('sorted:', f.get_sorted())
EOF
```

This implementation is not fully optimised but demonstrates a different
approach to sorting with fraglets.
98 changes: 98 additions & 0 deletions docs/INSTRUCTION_SET.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Fraglets Instruction Summary

This document summarizes the instruction set used by the Fraglets engine. It is derived from the `frag-instrset-20070924.txt` reference and the example programs included in this repository.

Fraglets are lists of tokens. The first token of a list acts as the instruction tag. Rules consume the instruction tag and transform the remaining tokens. When two fraglets "match," the rule bodies can combine or generate new fraglets.

## 1. Core Instructions

| Instruction | Effect |
|-------------|--------|
| `dup` | `[dup t a tail]` → `[t a a tail]` – duplicate a single symbol |
| `exch` | `[exch t a b tail]` → `[t b a tail]` – swap two tags |
| `fork` | `[fork a b tail]` → `[a tail]`, `[b tail]` – copy a fraglet with two different prefixes |
| `match` | `[match a tail1]` and `[a tail2]` → `[tail1 tail2]` – concatenate fraglets when their first symbols match |
| `matchp` | `[matchp a tail1]` and `[a tail2]` → `[matchp a tail1]`, `[tail1 tail2]` – catalytic match; the rule persists |
| `nop` | `[nop tail]` → `[tail]` – no operation |
| `nul` | `[nul tail]` → `[]` – destroy a fraglet |
| `pop2` | `[pop2 h t a b tail]` → `[h a]`, `[t b tail]` – pop the head of a list into a separate fraglet |
| `split` | `[split seq1 * seq2]` → `[seq1]`, `[seq2]` – break a fraglet at the first `*` |

## 2. Communication and Timing

| Instruction | Effect |
|-------------|--------|
| `broadcast` | `[broadcast seg tail]` → `n[tail]` – copy `tail` to all neighbours on segment `seg` |
| `delay` | `[delay n tail]` → `[tail]` after waiting `n` cycles |
| `send` | `[send seg dest tail]` → `dest[tail]` – send to a node on segment `seg`; special `stdout` and `stderr` destinations print `tail` |

## 3. Logic and Arithmetic

| Instruction | Effect |
|-------------|--------|
| `abs` | `[abs tag n tail]` → `[tag |n| tail]` |
| `div` | `[div tag n1 n2 tail]` → `[tag n1/n2 tail]`; removed if `n2` is zero |
| `empty` | `[empty yes no tail]` → `[yes]` if `tail` is empty, else `[no tail]` |
| `eq` | `[eq yes no n m tail]` → `[yes n m tail]` if equal, else `[no n m tail]` |
| `length` | `[length t1 tail]` → `[t1 |tail| tail]` |
| `lt` | `[lt yes no n m tail]` → `[yes n m tail]` if `n < m`, else `[no n m tail]` |
| `mod` | `[mod tag n1 n2 tail]` → `[tag (n1 % n2) tail]` |
| `mult` | `[mult tag n1 n2 tail]` → `[tag (n1 * n2) tail]` |
| `pow` | `[pow tag n1 n2 tail]` → `[tag (n1 ^ n2) tail]` |
| `sub` | `[sub tag n1 n2 tail]` → `[tag (n1 - n2) tail]` |
| `sum` | `[sum tag n1 n2 tail]` → `[tag (n1 + n2) tail]` |

## 4. Reserved Keywords

`*` – used by `split` to mark the separation point
`stdout`, `stderr` – pseudo destinations for `send`
`stdin`, `out`, `_`, `__` – reserved for future extensions

## 5. Experimental Extensions

| Instruction | Effect |
|-------------|--------|
| `anycast` | `[anycast seg tail]` → `n[tail]` – copy to at most one neighbour |
| `copy` | `[copy tail]` → `[tail]2` |
| `pop` | `[pop h a b c]` → `[h b c]` (proposed rename to `tail`) |
| `printsym` | `[printsym sym tail]` → `[tail]` while printing `sym` |
| `wait` | `[wait a b c]` → `[a b c]` after 10 cycles; prefer `delay` |
| `splitat` | `[splitat | a b c | x y z]` → `[a b c]`, `[x y z]` – split at a marker |
| `newnode` | `m[newnode ch n tail]` → `a n ch`, `n[tail]` – create child node |
| `inject` | `n1[inject n2 tail]` → `n2[tail]` (child) |
| `expel` | `n2[expel tail]` → `n1[tail]` (parent) |
| `newname` | `[newname tag s1 s2 tail]` → `[tag s1s2 tail]` |

Deprecated instruction: `diff` (use `sub` and `abs` instead).

## Example – Selection Sort

The repository includes `sort.fra`, which implements a selection sort using these instructions. Key rules:

```fraglets
[matchp sort empty finish continue]
[matchp continue split remain * getmin]
[matchp min split match remain sort * split match sorted match tosorted sorted * tosorted]
```

The `getmin` function repeatedly uses `matchp`, `lt`, and `pop2` to find the smallest number and store the rest in a `remain` list.

To run the example via Python:

```bash
python3 -m pip install . # build the cFraglets module
python3 test_sort.py
```

This parses `sort.fra`, executes the fraglet engine for a fixed number of iterations, and verifies that the resulting list is sorted.

Refer to `README.md` for build instructions and `test_sort.py` for a complete usage example.

## Experimental QuickSort

`quicksort.fra` demonstrates how the same primitives can implement a recursive
quicksort. The program uses persistent rules to split the list around a pivot,
recursively sort the `less` and `greater` partitions and finally merge the
results. It serves as a more advanced example of fraglet programming but has not
been extensively tuned.

32 changes: 32 additions & 0 deletions quicksort.fra
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# experimental quicksort implementation
# [qsort <list>] -> [sorted <list>]

[sorted]

# Entry rule: if the list is empty we're done, otherwise continue
[matchp qsort empty qs_finish qs_partition]

# Split first element as pivot and the rest of the list
[matchp qs_partition pop2 pivot qs_part]

# Partition rest into [less] and [greater] lists
[matchp qs_part empty qs_recur qs_compare]
[matchp qs_compare pop2 cur qs_part2]
[matchp qs_part2 lt qs_lt qs_ge pivot cur]
[matchp qs_lt match less less]
[matchp qs_ge match greater greater]

# After processing current element, continue partitioning
[matchp qs_lt match qs_part qs_compare]
[matchp qs_ge match qs_part qs_compare]

# When done partitioning, recursively sort less and greater
[matchp qs_recur fork less_sort greater_sort]
[matchp less_sort match less qsort]
[matchp greater_sort match greater qsort]

# Merge results: sorted less list, pivot, sorted greater list
[matchp qs_finish split match less sorted * split match greater sorted * match pivot match sorted match tosorted sorted * tosorted]

# example invocation
[qsort 5 1 4 2 3]
1 change: 0 additions & 1 deletion sort.fra
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,3 @@

[sort 203 -200 989 -446 -927 962 -485 714 -351 226 -791 55 448 -32 -477 261 529 38 922 -419 822 -395 -757 -951 757 -521 88]

# [pop2 pop2 pop2]