11 | This is the browsable standard library of the bruijn programming language. Follow any of the links below to see the source of the corresponding module.
12 |
13 | LINKS
14 |
15 |
16 |
STATS
17 |
18 |
19 |
--------------------------------------------------------------------------------
/samples/rosetta/harmonic_series.bruijn:
--------------------------------------------------------------------------------
1 | :import std/List .
2 | :import std/Combinator .
3 | :import std/Math/Rational Q
4 | :import std/Number N
5 |
6 | harmonic [0 &[[(Q.add 1 op) : N.++0]] start [[1]]] ⧗ Unary → Rational
7 | op (+1) : N.--0
8 | start (+0.0q) : (+1)
9 |
10 | custom-gt? &[[[N.gt? 2 (N.mul 0 N.++1)]]] ⧗ Rational → Νumber → Boolean
11 |
12 | main [φ cons first-20 first-10-above (harmonic <$> (iterate [[[1 (2 1 0)]]] (+0u)))]
13 | first-20 take (+20)
14 | first-10-above [take (+10) first-above]
15 | first-above [find-index [custom-gt? 0 1] 1] <$> (iterate N.inc (+0))
16 |
--------------------------------------------------------------------------------
/samples/fun/deepthought_state.bruijn:
--------------------------------------------------------------------------------
1 | # state monad example: logger/writer
2 |
3 | :import std/Combinator .
4 | :import std/List .
5 | :import std/Number .
6 | :import std/Monad/State .
7 |
8 | log [[[[0 (1 ++ 2) 3]]]]
9 |
10 | deepthought answer >>= [correct >>= [check >>= [return]]]
11 | answer log (+42) "Finding answer... "
12 | correct log (0 =? (+42)) "Checking answer... "
13 | check 0 (log [[0]] "Is correct!") (log [[0]] "Is false!")
14 | return pure 2
15 |
16 | :test (deepthought [[0]]) ([0 (+42) "Finding answer... Checking answer... Is correct!"])
17 |
18 | main [deepthought [[0]] [[0]]]
19 |
--------------------------------------------------------------------------------
/samples/rosetta/function_composition.bruijn:
--------------------------------------------------------------------------------
1 | :import std/Number .
2 |
3 | # composition, bluebird combinator
4 | …∘… [[[2 (1 0)]]]
5 |
6 | :test (((inc ∘ (mul (+2))) (+3)) =? (+7)) ([[1]])
7 |
8 | # 2x composition, blackbird combinator
9 | …∘∘… [[[[3 (2 1 0)]]]]
10 |
11 | :test (((inc ∘∘ mul) (+2) (+3)) =? (+7)) ([[1]])
12 |
13 | # 3x composition, bunting combinator
14 | …∘∘∘… [[[[[4 (3 2 1 0)]]]]]
15 |
16 | :test (((inc ∘∘∘ (add ∘∘ mul)) (+1) (+2) (+4)) =? (+7)) ([[1]])
17 |
18 | # reverse composition, queer bird combinator
19 | …→… [[[1 (2 0)]]]
20 |
21 | :test ((((mul (+2)) → inc) (+3)) =? (+7)) ([[1]])
22 |
--------------------------------------------------------------------------------
/samples/fun/rng-state.bruijn:
--------------------------------------------------------------------------------
1 | # MIT License, Copyright (c) 2024 Marvin Borner
2 | # generates three pseudo-random integers using a state monad
3 | # printf | bruijn rng-state.bruijn
4 |
5 | :import std/Combinator .
6 | :import std/String .
7 | :import std/Number .
8 | :import std/Monad/State .
9 |
10 | max (+1000)
11 |
12 | rand [[[0 1 1]] rng]
13 | rng (+1103515245) ⋅ 0 + (+12345) % max
14 |
15 | rand-bool map even? rand
16 |
17 | # normal bind
18 | triple rand >>= [rand >>= [rand >>= [pure [0 1 2 3]]]]
19 |
20 | :test ((triple (+50) [[1]] [[[0]]]) =? (+595)) ([[1]])
21 |
22 | main string→number → triple → &[[1]]
23 |
--------------------------------------------------------------------------------
/samples/rosetta/fibonacci_sequence.bruijn:
--------------------------------------------------------------------------------
1 | :import std/Combinator .
2 | :import std/Math .
3 | :import std/List .
4 |
5 | # unary/Church fibonacci (moderately fast but very high space complexity)
6 | fib-unary [0 [[[2 0 [2 (1 0)]]]] k i]
7 |
8 | :test (fib-unary (+6u)) ((+8u))
9 |
10 | # ternary fibonacci using infinite list iteration (very fast)
11 | fib-list index fibs
12 | fibs head <$> (iterate &[[0 : (1 + 0)]] ((+0) : (+1)))
13 |
14 | :test (fib-list (+6)) ((+8))
15 |
16 | # recursive fib (very slow)
17 | fib-rec y [[0 (+1) (+0) (0 (+2) (+1) rec)]]
18 | rec (1 --0) + (1 --(--0))
19 |
20 | :test (fib-rec (+6)) ((+8))
21 |
--------------------------------------------------------------------------------
/samples/euler/009.bruijn:
--------------------------------------------------------------------------------
1 | :import std/Combinator .
2 | :import std/Logic .
3 | :import std/Math .
4 | :import std/List .
5 |
6 | # solve' [head <#> lst]
7 | # check (2 + 1 + 0 =? 3) ⋀? ((0 ** (+2)) + (1 ** (+2)) =? (2 ** (+2))) (true : (2 ⋅ 1 ⋅ 0)) {}false
8 | # lst concat-map [concat-map [map [check] ({ (+1) → --0 })] ({ (+1) → --0 })] ({ (+1) → 0 })
9 |
10 | solve [head <#> lst] → head → tail
11 | check [[[[[2 + 1 + 0 =? 7 (true : (2 ⋅ 1 ⋅ 0)) {}false]]] a b c]] (1 ** (+2)) (0 ** (+2))
12 | a 1 - 0
13 | b (+2) ⋅ 3 ⋅ 2
14 | c 1 + 0
15 | lst [[check] <$> ({ (+1) → --0 })] <++> ({ (+2) → (sqrt 0) })
16 |
17 | main [solve (+1000)]
18 |
--------------------------------------------------------------------------------
/docs/wiki_src/introduction/animations.md:
--------------------------------------------------------------------------------
1 | # Animations
2 |
3 | ## Id substitution
4 |
5 |
7 |
8 | ## Omega beta reduction
9 |
10 |
12 |
--------------------------------------------------------------------------------
/samples/rosetta/binary_search.bruijn:
--------------------------------------------------------------------------------
1 | :import std/Combinator .
2 | :import std/List .
3 | :import std/Math .
4 | :import std/Option .
5 |
6 | binary-search [y [[[[[2 3 none go]]]]] (+0) --(∀0) 0]
7 | go [compare-case eq lt gt (2 !! 0) 1] /²(3 + 2)
8 | eq some 0
9 | lt 5 4 --0 2 1
10 | gt 5 ++0 3 2 1
11 |
12 | # example using sorted list of x^3, x=[-50,50]
13 | find [[map-or "not found" [0 : (1 !! 0)] (binary-search 0 1)] lst]
14 | lst take (+100) ((\pow (+3)) <$> (iterate ++‣ (-50)))
15 |
16 | :test (find (+100)) ("not found")
17 | :test ((head (find (+125))) =? (+55)) ([[1]])
18 | :test ((head (find (+117649))) =? (+99)) ([[1]])
19 |
20 | main [[0]]
21 |
--------------------------------------------------------------------------------
/samples/euler/004.bruijn:
--------------------------------------------------------------------------------
1 | # TODO: This has serious performance problems
2 |
3 | :import std/Combinator .
4 | :import std/List .
5 | :import std/Math .
6 |
7 | # palindrome? S.number→string → [S.eq? S.<~>0 0]
8 |
9 | palindrome? [y [[[&[[>?2 (4 (3 ⋅ (+10) + 0) 1) (3 =? 5)]] (quot-rem 0 (+10))]]] (+0) 0]
10 |
11 | # combs [[S.concat-map [S.map (mul 0) ({ 0 → 1 })] ({ ((+10) ** --1) → 0 })] --((+10) ** 0)]
12 |
13 | solve [[y [[[0 =? 3 1 rec]]] empty ((+10) ** --1)] ((+10) ** 0)] → lmax
14 | rec 2 (comb ++ 1) ++0
15 | comb y [[[0 =? 6 1 (2 ([palindrome? 0 (0 : 2) 2] (3 ⋅ 0)) ++0)]]] empty 0
16 |
17 | :test ((solve (+2)) =? (+9009)) ([[1]])
18 |
19 | main [solve (+3)]
20 |
--------------------------------------------------------------------------------
/samples/rosetta/hailstone_sequence.bruijn:
--------------------------------------------------------------------------------
1 | :import std/Combinator .
2 | :import std/List .
3 | :import std/Math M
4 | :import std/Number/Binary .
5 |
6 | # hailstone sequence using binary shifts
7 | hailstone y [[(0 =? (+1b)) {}0 go]]
8 | go 0 : (=²?0 (1 /²0) (1 (↑¹0 + 0)))
9 |
10 | # --- tests ---
11 |
12 | seq-27 hailstone (+27b)
13 |
14 | :test (∀seq-27) ((+112))
15 | :test (take (+4) seq-27) ((+27b) : ((+82b) : ((+41b) : {}(+124b))))
16 | :test (take (+4) <~>seq-27) ((+1b) : ((+2b) : ((+4b) : {}(+8b))))
17 |
18 | all-below-100000 [0 : ∀(hailstone 0)] <$> seq
19 | seq take (+99999) (iterate ++‣ (+1b))
20 |
21 | main [head (max-by (M.compare ⋔ tail) all-below-100000)]
22 |
--------------------------------------------------------------------------------
/docs/code.css:
--------------------------------------------------------------------------------
1 | code, pre {
2 | tab-size: 4;
3 | }
4 |
5 | .pp {
6 | color: #ff64bd;
7 | }
8 |
9 | .definition {
10 | color: #13dbee;
11 | }
12 |
13 | .comment {
14 | color: #999999;
15 | }
16 |
17 | .type {
18 | color: #ffa120;
19 | }
20 |
21 | .abstraction {
22 | color: #6b82ff;
23 | }
24 |
25 | .application {
26 | color: #ff8750;
27 | }
28 |
29 | .index {
30 | color: #ff5050;
31 | }
32 |
33 | .number, .float, .complex {
34 | color: #b1ee13;
35 | }
36 |
37 | .string, .char {
38 | color: #bb73f0;
39 | }
40 |
41 | .code-highlight {
42 | filter: saturate(5) brightness(3);
43 | -webkit-text-stroke-width: 0.1ex;
44 | }
45 |
46 | .code-clickable {
47 | cursor: pointer;
48 | }
49 |
--------------------------------------------------------------------------------
/std/Set.bruijn:
--------------------------------------------------------------------------------
1 | # MIT License, Copyright (c) 2023 Marvin Borner
2 | # Generic set implementation using AVL trees
3 | # some functions require a compare-case argument!
4 |
5 | :import std/Tree/Balanced T
6 | :import std/List .
7 |
8 | # empty set
9 | empty T.empty ⧗ Set
10 |
11 | # adds a number of a set
12 | add T.insert ⧗ Compare → Number → Set → Set
13 |
14 | # returns true if a number is in a set
15 | has? T.has? ⧗ Compare → Number → Set → Boolean
16 |
17 | # count of elements in set
18 | size T.size ⧗ Set → Number
19 |
20 | # converts a set to a list
21 | set→list T.tree→list ⧗ Set → (List Number)
22 |
23 | # converts a list to a set
24 | list→set T.list→tree ⧗ Compare → (List Number) → Set
25 |
--------------------------------------------------------------------------------
/editors/vim/README.md:
--------------------------------------------------------------------------------
1 | # Vim syntax highlighting
2 |
3 | ## Install manually
4 |
5 | Copy or symlink this directory to your vim plugins directory, e.g. using
6 | the following command inside this directory:
7 |
8 | mkdir -p $HOME/.vim/pack/plugins/start/
9 | ln -s $PWD $HOME/.vim/pack/plugins/start/bruijn
10 |
11 | ## Install with a plugin manager
12 |
13 | In this example using vim-plug (others should work similarly):
14 |
15 | 1. Add `Plug 'marvinborner/bruijn', { 'rtp': 'editors/vim' }` to your
16 | `.vimrc`
17 | 2. Run `:PlugInstall`
18 |
19 | # Autocompletion
20 |
21 | This plugin uses vim’s `abbreviation`s for autocompletion. You can
22 | complete them using `space` or `C-]`
23 |
--------------------------------------------------------------------------------
/std/test_all.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | if [ -z "$1" ]; then
4 | echo "Usage: $0 "
5 | exit 1
6 | fi
7 |
8 | echo "# useful for running all tests of the standard library" >All.bruijn
9 | echo >>All.bruijn
10 |
11 | FILES="$(find * -type f -name "*.bruijn" ! -name "All.bruijn" ! -path "*Generic*")"
12 |
13 | for f in $FILES; do
14 | echo ":import std/${f%*.bruijn} ." >>All.bruijn
15 | done
16 |
17 | # for ci, just run `bruijn All.bruijn`
18 | echo >>All.bruijn
19 | echo "main [[0]]" >>All.bruijn
20 |
21 | if cat /dev/null | bruijn All.bruijn -r "$1" | tee /dev/fd/2 | grep -q "ERROR"; then
22 | exit 1
23 | fi
24 |
25 | hyperfine --warmup 5 --runs 20 "cat /dev/null | bruijn -r $1 All.bruijn"
26 |
--------------------------------------------------------------------------------
/samples/aoc/2022/02/solve.bruijn:
--------------------------------------------------------------------------------
1 | # perfectly normal rock paper scissors :)
2 |
3 | :import std/Combinator .
4 | :import std/String .
5 | :import std/Number .
6 | :import std/Number/Conversion .
7 |
8 | :import std/Char C
9 | :import std/Number/Binary B
10 |
11 | parse (split-by (C.eq? ' ')) → &[[²³(B.sub ^1 'A') : ²³(B.sub ^0 'X')]]
12 |
13 | solve [foldr go (+0)]
14 | go &[[[^3 !! 1 + ((drop 0 ^(~3)) ++ (take 0 ^(~3)) !! 1)] (~(~2) !! 1)]] → add
15 |
16 | main lines → (map parse) → (φ cons part1 part2)
17 | part1 solve (((+1) : ((+2) : {}(+3))) : (((+0) : ((+3) : {}(+6))) : ((+1) : ((+0) : {}(+2)))))
18 | part2 solve (((+0) : ((+3) : {}(+6))) : (((+3) : ((+1) : {}(+2))) : ((+0) : ((+1) : {}(+2)))))
19 |
--------------------------------------------------------------------------------
/src/Reducer.hs:
--------------------------------------------------------------------------------
1 | -- MIT License, Copyright (c) 2024 Marvin Borner
2 | module Reducer
3 | ( reduce
4 | , reduceNoIO
5 | ) where
6 |
7 | import Config
8 | import Helper
9 | import qualified Reducer.HigherOrder as HigherOrder
10 | import qualified Reducer.ION as ION
11 | import qualified Reducer.RKNL as RKNL
12 |
13 | reduce :: EvalConf -> Expression -> IO Expression
14 | reduce conf e = case _reducer conf of
15 | "RKNL" -> RKNL.reduce e
16 | "ION" -> pure $ ION.reduce e
17 | "HigherOrder" -> pure $ HigherOrder.reduce e
18 | _ -> error "Invalid reducer"
19 |
20 | reduceNoIO :: Expression -> Expression
21 | reduceNoIO = HigherOrder.reduce
22 |
--------------------------------------------------------------------------------
/std/Number/Wadsworth.bruijn:
--------------------------------------------------------------------------------
1 | # MIT License, Copyright (c) 2023 Marvin Borner
2 | # from Wadsworth's "some unusual numeral systems" (p224, see refs in README)
3 |
4 | :import std/Combinator .
5 |
6 | zero [0 [k]] ⧗ Wadsworth
7 |
8 | # increment Wadsworth number
9 | inc [[[2 [2 (1 0) 1]]]] ⧗ Wadsworth → Wadsworth
10 |
11 | :test (inc (inc zero)) ([[[2 (1 (0 (k [[1]]))) 1 0]]])
12 |
13 | # decrement Wadsworth number
14 | dec [[1 [k (1 0)] i]] ⧗ Wadsworth → Wadsworth
15 |
16 | :test (dec (dec (inc (inc zero)))) (zero)
17 |
18 | # returns true if Wadsworth number is zero
19 | zero? [0 i (k (k [[0]]))] ⧗ Wadsworth → Bool
20 |
21 | :test (zero? zero) ([[1]])
22 | :test (zero? (inc zero)) ([[0]])
23 | :test (zero? (inc (inc zero))) ([[0]])
24 |
--------------------------------------------------------------------------------
/samples/fun/jottary.bruijn:
--------------------------------------------------------------------------------
1 | # a small Jottary (unary Jot) interpreter
2 | # also serves as example usage of monadic list operations
3 | # run with "printf 1...1 | bruijn jottary.bruijn"
4 |
5 | :import std/Combinator .
6 | :import std/List .
7 | :import std/Monad .
8 | :import std/Number .
9 |
10 | go [eval-r (<~>((concat huh) !! 0) ; i)]
11 | huh (\replicate-m (l : {}r)) <$> (iterate ++‣ (+0))
12 | l [(0 s) k]
13 | r [s (k 0)]
14 |
15 | :test (go (+0)) (i)
16 | :test (go (+1)) ((i s) k)
17 | :test (go (+2)) (s (k i))
18 | :test (go (+3)) (i s k s k)
19 | :test (go (+4)) (s (k (i s k)))
20 | :test (go (+5)) (((s (k i)) s) k)
21 | :test (go (+6)) (s (k (s (k i))))
22 | :test (go (+59)) (k)
23 | :test (go (+503)) (s)
24 |
25 | main go ∘ length
26 |
--------------------------------------------------------------------------------
/std/Number/SK.bruijn:
--------------------------------------------------------------------------------
1 | # MIT License, Copyright (c) 2025 Marvin Borner
2 | # by https://john-tromp.medium.com/sk-numerals-9ad1b5634b28
3 | # and proposed by Stephen Wolfram in "Combinators: A Centennial View"
4 |
5 | :import std/Combinator .
6 | :import std/Tuples .
7 |
8 | # -4 [[[[3]]]]
9 | # -3 [[[[2]]]]
10 | # -2 [[[2]]]
11 | # -1 [[[1]]]
12 | # 0 [[1]]
13 | # +1 [[0]]
14 | # +2 [[1 0]]
15 | # +3 [[0 (1 0)]]
16 | # +4 [[(1 0) (0 (1 0))]]
17 |
18 | # +n has fib(n) of 1s
19 |
20 | zero k ⧗ SK
21 |
22 | inc s ⧗ SK → SK
23 |
24 | ++‣ inc
25 |
26 | dec [[[2 (k 0) 1]]] ⧗ SK → SK
27 |
28 | --‣ dec
29 |
30 | :test (dec (inc zero)) (zero)
31 |
32 | # only for positive numbers (also see Bruijn.bruijn!)
33 | zero? ki : i : s : k ⧗ SK → Boolean
34 |
35 | =?‣ zero?
36 |
--------------------------------------------------------------------------------
/samples/fun/wilsons_primes.bruijn:
--------------------------------------------------------------------------------
1 | # 208-bit golfed characteristic prime sequence using Wilson's theorem
2 | # (Tromp's prime sieve only takes 143 bit)
3 |
4 | # b' not-zero? mod
5 | zmod [[1 [0 [[1]]] (1 [1 [[[0 (2 [[1]]) 1]]] [1] [[0]]] [[[0]]]) [[0]]]]
6 |
7 | zero? [0 [[[0]]] [[1]]] ⧗ Unary → Boolean
8 |
9 | # experiment with repeated subtraction (flipped args, zero? not golfed yet)
10 | # TODO: consider Parigot for trivial predecessor?
11 | zmod' [[0 0] [[[zero? (0 3 1) (2 2 (0 1 3)) (zero? 1)] sub]]]
12 | sub [[0 [[[2 [[0 (1 3)]] [1] [0]]]] 1]]
13 |
14 | # y [[[(zmod 1 0) : (2 (1 ⋅ (0 ⋅ 0)) ++0)]]] (+1u) (+1u)
15 | loop [0 0] [[[[0 (2 [0 [[1]]] (2 [2 [[[0 (2 [[1]]) 1]]] [1] [[0]]] [[[0]]]) [[0]]) (3 3 [3 (2 (2 0))] [[1 (3 1 0)]])]]]] (+1u) (+1u)
16 |
17 | main [loop]
18 |
--------------------------------------------------------------------------------
/stack.yaml.lock:
--------------------------------------------------------------------------------
1 | # This file was autogenerated by Stack.
2 | # You should not edit this file by hand.
3 | # For more information, please see the documentation at:
4 | # https://docs.haskellstack.org/en/stable/lock_files
5 |
6 | packages:
7 | - completed:
8 | hackage: bitstring-0.0.0@sha256:7638c1c515d728a84507e33c830854cfd141a5bcabec6963e68f52baf27979e9,1248
9 | pantry-tree:
10 | sha256: 2006295f9a9943177952b9b0bfee7de96d5f86d8f989de7dd154159705fa2572
11 | size: 284
12 | original:
13 | hackage: bitstring-0.0.0
14 | snapshots:
15 | - completed:
16 | sha256: e2c529ccfb21501f98f639e056cbde50470b86256d9849d7a82d414ca23e4276
17 | size: 712898
18 | url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/22/12.yaml
19 | original: lts-22.12
20 |
--------------------------------------------------------------------------------
/samples/aoc/2019/01/solve.bruijn:
--------------------------------------------------------------------------------
1 | :import std/Combinator .
2 | :import std/Logic .
3 | :import std/String .
4 | :import std/Math .
5 |
6 | fuel [/³*0 - (+2)] ⧗ Mass → Fuel
7 |
8 | :test ((fuel (+12)) =? (+2)) (true)
9 | :test ((fuel (+14)) =? (+2)) (true)
10 | :test ((fuel (+1969)) =? (+654)) (true)
11 | :test ((fuel (+100756)) =? (+33583)) (true)
12 |
13 | fuelfuel z [[rec]] ⧗ Mass → Fuel
14 | rec go (fuel 0)
15 | go [>?0 (0 + (2 0)) (+0)]
16 |
17 | :test ((fuelfuel (+14)) =? (+2)) (true)
18 | :test ((fuelfuel (+1969)) =? (+966)) (true)
19 | :test ((fuelfuel (+100756)) =? (+50346)) (true)
20 |
21 | part1 ∑‣ ∘ (map fuel) ⧗ (List Mass) → Fuel
22 |
23 | part2 ∑‣ ∘ (map fuelfuel) ⧗ (List Mass) → Fuel
24 |
25 | main [parts nums]
26 | nums string→number <$> ~(<~>(lines 0))
27 | parts [(part1 0) : (part2 0)]
28 |
--------------------------------------------------------------------------------
/samples/aoc/2023/02/solve.bruijn:
--------------------------------------------------------------------------------
1 | :import std/Combinator .
2 | :import std/String .
3 | :import std/Math .
4 | :import std/Char C
5 |
6 | valid? &[[\(C.eq? 0 'r' (1 >? (+12)) (C.eq? 0 'g' (1 >? (+13)) (1 >? (+14))))]]
7 |
8 | parse map (game → (map (sets → (map (cubes → (map (tail → cube)))) → concat → maxes)) → head)
9 | game (split-list-by (C.eq? ':')) → tail
10 | sets split-list-by (C.eq? ';')
11 | cubes split-list-by (C.eq? ',')
12 | cube (split-list-by (C.eq? ' ')) → &[[(string→number 1) : ^(^0)]]
13 | maxes [(m 'r') : ((m 'g') : {}(m 'b'))]
14 | m [max-by (compare ⋔ head) (filter [C.eq? (tail 0) 1] 1)]
15 |
16 | main lines → parse → enumerate → (φ cons part1 part2)
17 | part1 (map &[[1 : (all? valid? 0)]]) → (filter tail) → (map (head → inc)) → sum
18 | part2 (map &[(map head) → product]) → sum
19 |
--------------------------------------------------------------------------------
/std/Set/Number.bruijn:
--------------------------------------------------------------------------------
1 | # MIT License, Copyright (c) 2024 Marvin Borner
2 |
3 | :input std/Set
4 |
5 | :import std/Number T
6 |
7 | # adds a number of a set
8 | add T.>add ⧗ Number → NumberSet → NumberSet
9 |
10 | # returns true if a number is in a set
11 | has? T.>has? ⧗ Number → NumberSet → Boolean
12 |
13 | :test (has? (+5) (add (+5) empty)) ([[1]])
14 | :test (has? (+5) empty) ([[0]])
15 |
16 | # converts a list to a set
17 | list→set T.>list→set ⧗ (List Number) → NumberSet
18 |
19 | :test (has? (+0) (list→set ((+5) : ((+3) : ((+2) : ((+1) : {}(+0))))))) ([[1]])
20 | :test (has? (+5) (list→set ((+5) : ((+3) : ((+2) : ((+1) : {}(+0))))))) ([[1]])
21 | :test (has? (+6) (list→set ((+5) : ((+3) : ((+2) : ((+1) : {}(+0))))))) ([[0]])
22 | :test (has? (+7) (list→set ((+5) : ((+7) : {}(+1))))) ([[1]])
23 |
--------------------------------------------------------------------------------
/std/Set/String.bruijn:
--------------------------------------------------------------------------------
1 | # MIT License, Copyright (c) 2024 Marvin Borner
2 | # TODO: hash instead of comparing
3 |
4 | :input std/Set
5 |
6 | :import std/String S
7 |
8 | # adds a number of a set
9 | add S.>add ⧗ String → StringSet → StringSet
10 |
11 | # returns true if a number is in a set
12 | has? S.>has? ⧗ String → StringSet → Boolean
13 |
14 | :test (has? "abc" (add "abc" empty)) ([[1]])
15 | :test (has? "abc" empty) ([[0]])
16 |
17 | # converts a list to a set
18 | list→set S.>list→set ⧗ (List String) → StringSet
19 |
20 | :test (has? "0" (list→set ("a" : ("b" : ("d" : ("c" : {}"0")))))) ([[1]])
21 | :test (has? "a" (list→set ("a" : ("b" : ("d" : ("c" : {}"0")))))) ([[1]])
22 | :test (has? "e" (list→set ("a" : ("b" : ("d" : ("c" : {}"0")))))) ([[0]])
23 | :test (has? "c" (list→set ("a" : ("c" : {}"b")))) ([[1]])
24 |
--------------------------------------------------------------------------------
/samples/fun/pi-rational.bruijn:
--------------------------------------------------------------------------------
1 | :import std/Pair .
2 | :import std/Combinator .
3 | :import std/Number/Unary .
4 |
5 | qadd &[[&[[p : q]]]] ⧗ Rational → Rational → Rational
6 | p (3 ⋅ ++0) + (1 ⋅ ++2)
7 | q (2 ⋅ 0) + (2 + 0)
8 |
9 | fac [[1 [[0 (1 [[2 1 (1 0)]])]] [1] i]] ⧗ Unary → Unary
10 |
11 | # challenge: smallest, yet fastest converging, π ratio calculation
12 |
13 | # naive
14 | v1-pi/2 [0 &[[op]] start [[1]]]
15 | start ((+0u) : (+0u)) : (+0u)
16 | op (qadd 1 (enum : --denom)) : ++0
17 | enum (0 (+2u)) ⋅ ((+2u) (fac 0))
18 | denom fac ++((+2u) ⋅ 0)
19 |
20 | dfac [[1 [[0 (1 [[2 1 (1 (1 0))]])]] [1] i]] ⧗ Unary → Unary
21 |
22 | # double factorial
23 | # dfac and fac could be shared!
24 | v2-pi/2 [0 &[[op]] start [[1]]]
25 | start ((+0u) : (+0u)) : (+0u)
26 | op (qadd 1 (enum : --denom)) : ++0
27 | enum fac 0
28 | denom dfac ++0
29 |
--------------------------------------------------------------------------------
/docs/wiki_src/coding/currying.md:
--------------------------------------------------------------------------------
1 | # Currying
2 |
3 | Lambda calculus naturally supports currying -- that is, only *partially*
4 | applying a function. In fact *any* function can be applied with *any*
5 | amount of arguments!
6 |
7 | In bruijn, currying is a great way to make functions even more elegant.
8 |
9 | For example, take the negation function:
10 |
11 | ``` bruijn
12 | # subtracts argument from zero
13 | -‣ [(+0) - 0] ⧗ Number → Number
14 |
15 | # equivalent curried version
16 | -‣ sub (+0)
17 | ```
18 |
19 | Currying is also very useful for higher-order functions.
20 |
21 | Multiplying values in a list by partially applying the `mul`{.bruijn}
22 | function:
23 |
24 | ``` bruijn
25 | # doubles numbers in a list
26 | double-list [0 <$> (mul (+2))] ⧗ (List Number) → (List Number)
27 |
28 | :test (double-list ((+1) : {}(+2))) ((+2) : {}(+4))
29 | ```
30 |
--------------------------------------------------------------------------------
/std/Number.bruijn:
--------------------------------------------------------------------------------
1 | # MIT License, Copyright (c) 2023 Marvin Borner
2 | # this is just a reference to the ternary implementation
3 | # read the wiki for the reasoning of using balanced ternary by default
4 |
5 | :import std/List .
6 |
7 | :input std/Number/Ternary
8 |
9 | # the following functions are only here because of recursive imports of list/ternary
10 |
11 | # converts number to list of its digits
12 | number→list [=?0 {}(+0) (z [[[rec]]] empty 0)] ⧗ Number → (List Number)
13 | rec =?0 case-end case-rec
14 | case-rec &[[4 (0 : 3) 1]] (quot-rem 0 (+10))
15 | case-end 1
16 |
17 | :test (number→list (+0)) ({}(+0))
18 |
19 | # converts a list of digits into a balanced ternary number
20 | list→number foldl [[(+10) ⋅ 1 + 0]] (+0) ⧗ (List Number) → Number
21 |
22 | :test (list→number ((+4) : ((+2) : {}(+0)))) ((+420))
23 | :test (list→number empty) ((+0))
24 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Test standard library
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | permissions:
9 | contents: read
10 |
11 | jobs:
12 | test:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v4
16 | - uses: freckle/stack-action@v5
17 | - name: Install hyperfine
18 | run: sudo apt-get install hyperfine
19 | - name: Install bruijn
20 | run: stack install
21 | - name: Run HigherOrder tests and measure time
22 | run: cd std/ && ./test_all.sh HigherOrder
23 | - name: Run RKNL tests and measure time
24 | run: cd std/ && ./test_all.sh RKNL
25 | # - name: Run ION tests and measure time
26 | # run: cd std/ && ./test_all.sh ION
27 | - name: Run HigherOrder sample tests
28 | run: cd samples/ && ./test_all.sh HigherOrder
29 |
--------------------------------------------------------------------------------
/docs/wiki_src/coding/prefix.md:
--------------------------------------------------------------------------------
1 | # Prefix
2 |
3 | Prefix functions are symbols written directly in front of another term
4 | (without space). The term gets applied as an argument to the prefix
5 | function. Use [mixfix functions](mixfix.md) if the function has more
6 | than one argument.
7 |
8 | They are defined by the `‣`{.bruijn} suffix.
9 |
10 | Example:
11 |
12 | ``` bruijn
13 | # defines a negation prefix function called '-'
14 | -‣ [(+0) - 0] ⧗ Number → Number
15 |
16 | # returns 0 - 10 = -10
17 | :test (-(+10)) ((-10))
18 | ```
19 |
20 | You can use them as normal functions by writing the identifier
21 | literally:
22 |
23 | ``` bruijn
24 | :test (-‣ (+10)) ((-10))
25 | ```
26 |
27 | ## Allowed characters
28 |
29 | Prefix functions can use any characters of `!?*@:;+-_#$%^&<>/\|{}~=` as
30 | well as mathematical unicode operators and arrows. They must be at least
31 | 1 character long.
32 |
--------------------------------------------------------------------------------
/std/Number/Scott.bruijn:
--------------------------------------------------------------------------------
1 | # MIT License, Copyright (c) 2023 Marvin Borner
2 |
3 | :import std/Combinator .
4 | :import std/Logic .
5 |
6 | zero [[1]] ⧗ Scott
7 |
8 | inc [[[0 2]]] ⧗ Scott → Scott
9 |
10 | :test (inc zero) ([[0 zero]])
11 | :test (inc (inc zero)) ([[0 [[0 zero]]]])
12 | :test (inc (inc (inc zero))) ([[0 [[0 [[0 zero]]]]]])
13 |
14 | dec [0 zero [0]] ⧗ Scott → Scott
15 |
16 | :test (dec zero) (zero)
17 | :test (dec (inc zero)) (zero)
18 | :test (dec (inc (inc zero))) (inc zero)
19 |
20 | zero? [0 true [false]] ⧗ Scott → Boolean
21 |
22 | :test (zero? zero) (true)
23 | :test (zero? (inc zero)) (false)
24 | :test (zero? (inc (inc zero))) (false)
25 |
26 | add z [[[1 0 [inc (3 0 1)]]]] ⧗ Scott → Scott → Scott
27 |
28 | :test (add zero zero) (zero)
29 | :test (add zero (inc zero)) (inc zero)
30 | :test (add (inc zero) zero) (inc zero)
31 | :test (add (inc zero) (inc zero)) (inc (inc zero))
32 |
--------------------------------------------------------------------------------
/docs/wiki_src/introduction/installation.md:
--------------------------------------------------------------------------------
1 | # Installation
2 |
3 | You first need to install Haskell's
4 | [Stack](https://docs.haskellstack.org/en/stable/) using their official
5 | instructions for your operating system. After that, clone the bruijn
6 | repository using
7 |
8 | ``` bash
9 | $ git clone https://github.com/marvinborner/bruijn
10 | $ cd bruijn
11 | ```
12 |
13 | Then, run bruijn using `stack run`{.bash}. This starts bruijn's
14 | [REPL](../coding/REPL.md). You can now play around with bruijn.
15 |
16 | Passing normal arguments to `bruijn`{.bash} with `stack`{.bash} can be
17 | done with `stack run -- [args]`. If you enjoy it, you can install
18 | `bruijn` into your stack path using `stack install`.
19 |
20 | ------------------------------------------------------------------------
21 |
22 | Please create an issue on
23 | [GitHub](https://github.com/marvinborner/bruijn/issues/new) if you
24 | encounter any issues.
25 |
--------------------------------------------------------------------------------
/samples/aoc/2016/01/solve.bruijn:
--------------------------------------------------------------------------------
1 | # vim first: :s/ //g
2 |
3 | :import std/Combinator .
4 | :import std/Char C
5 | :import std/String S
6 | :import std/Pair P
7 | :import std/Number .
8 | :import std/List .
9 |
10 | direction! [^0 : (S.string→number ~0)] ⧗ (List Char) → Direction
11 |
12 | :test (direction! "R42") ('R' : (+42))
13 |
14 | # north=0, east=1, south=2, west=3
15 | # rotation : (x : y)
16 | start ((+0) : ((+0) : (+0))) ⧗ State
17 |
18 | move [[go (C.eq? ^0 'R' ++‣ --‣ ^1)]] ⧗ State → Direction → State
19 | go [0 : (even? 0 y x)]
20 | x ^(~2) + (p ~1) : ~(~2)
21 | p 0 % (+4) =? (+3) -‣ [0]
22 | y ^(~2) : (~(~2) + (p ~1))
23 | p 0 % (+4) =? (+2) -‣ [0]
24 |
25 | :test (move start (direction! "R42")) ((+1) : ((+42) : (+0)))
26 |
27 | part1 (P.uncurry …+…) ∘ ~‣ ∘ (foldl move start) ⧗ (List Direction) → State
28 |
29 | main [parts coords]
30 | coords direction! <$> (split-list-by (C.eq? ',') (init 0))
31 | parts [part1 0]
32 |
--------------------------------------------------------------------------------
/samples/fun/mutrec.bruijn:
--------------------------------------------------------------------------------
1 | # some example usage of the variadic fixed point combinator
2 |
3 | :import std/List .
4 | :import std/Logic .
5 | :import std/Number .
6 |
7 | # generates odd? and even? function as list
8 | odd-even? y* (even? : {}odd?)
9 | even? [[[=?0 true (1 --0)]]]
10 | odd? [[[=?0 false (2 --0)]]]
11 |
12 | :test (^odd-even? (+5)) (false)
13 | :test (_odd-even? (+5)) (true)
14 |
15 | # calculates n % 3
16 | mod3 ^(y* (a : (b : {}c)))
17 | a [[[[=?0 (+0) (2 --0)]]]]
18 | b [[[[=?0 (+1) (1 --0)]]]]
19 | c [[[[=?0 (+2) (3 --0)]]]]
20 |
21 | :test ((mod3 (+0)) =? (+0)) (true)
22 | :test ((mod3 (+1)) =? (+1)) (true)
23 | :test ((mod3 (+2)) =? (+2)) (true)
24 | :test ((mod3 (+3)) =? (+0)) (true)
25 | :test ((mod3 (+4)) =? (+1)) (true)
26 | :test ((mod3 (+5)) =? (+2)) (true)
27 | :test ((mod3 (+6)) =? (+0)) (true)
28 | :test ((mod3 (+7)) =? (+1)) (true)
29 | :test ((mod3 (+8)) =? (+2)) (true)
30 | :test ((mod3 (+9)) =? (+0)) (true)
31 |
32 | main [[0]]
33 |
--------------------------------------------------------------------------------
/samples/rosetta/hamming_numbers.bruijn:
--------------------------------------------------------------------------------
1 | :import std/Combinator .
2 | :import std/Number .
3 | :import std/List .
4 |
5 | merge y [[[∅?1 0 (1 [[2 [[go]]]])]]]
6 | go 3 1 (3 : (6 2 4)) (1 : (6 5 0))
7 |
8 | # classic version while avoiding duplicate generation
9 | hammings-classic (+1) : (foldr u empty ((+2) : ((+3) : {}(+5))))
10 | u [[y [merge 1 ((mul 2) <$> ((+1) : 0))]]]
11 |
12 | :test ((hammings-classic !! (+42)) =? (+162)) ([[1]])
13 |
14 | # enumeration by a chain of folded merges (faster)
15 | hammings-folded ([(0 ∘ a) ∘ (0 ∘ b)] (foldr merge1 empty)) $ c
16 | merge1 [[1 [[1 : (merge 0 2)]]]]
17 | a iterate (map (mul (+5)))
18 | b iterate (map (mul (+3)))
19 | c iterate (mul (+2)) (+1)
20 |
21 | :test ((hammings-folded !! (+42)) =? (+162)) ([[1]])
22 |
23 | # --- output ---
24 |
25 | main [first-twenty : (n1691 : {}n1000000)]
26 | first-twenty take (+20) hammings-folded
27 | n1691 hammings-folded !! (+1690)
28 | n1000000 hammings-folded !! (+999999)
29 |
--------------------------------------------------------------------------------
/std/Generic/Monad.bruijn:
--------------------------------------------------------------------------------
1 | # MIT License, Copyright (c) 2024 Marvin Borner
2 | # for now, monads, functors, applicative, etc are all in here..
3 |
4 | # TODO: more monad interface
5 |
6 | :import std/List L
7 |
8 | liftA map ⧗ (a → b) → (M a) → (M b)
9 |
10 | liftA2 ap ∘∘ map ⧗ (a → b → c) → (M a) → (M b) → (M c)
11 |
12 | liftA3 [[[[(liftA2 3 2 1) <*> 0]]]] ⧗ (a → b → c → d) → (M a) → (M b) → (M c) → (M d)
13 |
14 | …<$… map ∘ k ⧗ a → (M b) → (M a)
15 |
16 | …*>… [[(i <$ 1) <*> 0]] ⧗ (M a) → (M b) → (M b)
17 |
18 | …<*… liftA2 k ⧗ (M a) → (M b) → (M a)
19 |
20 | …=<<… \bind
21 |
22 | …>>… [[1 >>= [1]]] ⧗ (M a) → (M b) → (M b)
23 |
24 | # TODO: traverse, sequence
25 | # in parser: string = traverse char!
26 |
27 | some [y [liftA2 L.cons 1 many]] ⧗ (M a) → (M (List a))
28 | many 0 <|> (pure L.empty)
29 |
30 | many [y [some <|> (pure L.empty)]] ⧗ (M a) → (M (List a))
31 | some liftA2 L.cons 1 0
32 |
33 | between [[[2 *> 0 <* 1]]] ⧗ (M a) → (M a) → (M a) → (M a)
34 |
--------------------------------------------------------------------------------
/std/Monad/State.bruijn:
--------------------------------------------------------------------------------
1 | # MIT License, Copyright (c) 2024 Marvin Borner
2 | # see samples/fun/rng-state for example usage
3 |
4 | :import std/Tuples .
5 | :import std/Combinator .
6 |
7 | # scala: s0 ⇒ { (s1, a) = run(s0); (s1, f(a)) }
8 | map [[[1 0 [[1 : (4 0)]]]]] ⧗ (a → b) → (State s a) → (State s b)
9 |
10 | …<$>… map
11 |
12 | # monadic bind/flat-map operator
13 | # scala: s0 ⇒ { (s1, a) = run(s0); f(a).run(s1) }
14 | bind [[[2 0 [[3 0 1]]]]] ⧗ (State s a) → (a → (State s b)) → (State s b)
15 |
16 | …>>=… bind
17 |
18 | pure tuple ⧗ a → (State s a)
19 |
20 | :test ((w' ∘ c) >>= [(w' ∘ c) >>= [pure 0]] [[0]]) (w' [[0]])
21 | :test ((w' ∘ c) >>= [(w' ∘ c) >>= [pure 0]] [[1]]) (w' [[1]])
22 |
23 | get [0 : 0]
24 |
25 | gets [[0 : (1 0)]]
26 |
27 | put [[1 : Ω]]
28 |
29 | puts [[(1 0) : Ω]]
30 |
31 | :test (bind get [bind (put ((+2u) 0)) [gets [(+2u) 0]]] (+2u)) ((+4u) : (+16u))
32 |
33 | # TODO
34 | ap [0]
35 |
36 | # TODO
37 | …<|>… [0]
38 |
39 | :input std/Generic/Monad
40 |
--------------------------------------------------------------------------------
/benchmarks/by-n/plot.py:
--------------------------------------------------------------------------------
1 | #!/bin/env python3
2 |
3 | import matplotlib.pyplot as plt
4 | import json
5 | import os, glob
6 |
7 |
8 | def render(file):
9 | data = json.load(open(file, "r"))["results"]
10 | basename = os.path.basename(file).split(".")[0]
11 | reducers = list({p["parameters"]["REDUCER"] for p in data})
12 | for reducer in reducers:
13 | labels = [
14 | p["parameters"]["N"]
15 | for p in data
16 | if p["parameters"]["REDUCER"] == reducer
17 | ]
18 | times = [
19 | p["median"] for p in data if p["parameters"]["REDUCER"] == reducer
20 | ]
21 | plt.plot(times)
22 | plt.xticks(range(len(labels)), labels)
23 | plt.legend(reducers)
24 | plt.title(f"reducer comparison for {basename}")
25 | plt.xlabel("N")
26 | plt.ylabel("Time (s)")
27 | plt.savefig(f"{basename}.png")
28 | plt.close()
29 |
30 |
31 | for file in glob.glob("*.json"):
32 | render(file)
33 |
--------------------------------------------------------------------------------
/std/Number/Pairing.bruijn:
--------------------------------------------------------------------------------
1 | # MIT License, Copyright (c) 2024 Marvin Borner
2 | # pairing functions for writing two integers as one, bijectionally
3 |
4 | :import std/Logic .
5 | :import std/Pair .
6 | :import std/Number .
7 | :import std/Math .
8 |
9 | # strong Rosenberg pairing function
10 | pair-ternary [[[0 ⋅ 0 + 0 + 2 - 1] (max 1 0)]] ⧗ Number → Number → Number
11 |
12 | :test ((pair-ternary (+0) (+0)) =? (+0)) (true)
13 | :test ((pair-ternary (+0) (+1)) =? (+1)) (true)
14 | :test ((pair-ternary (+1) (+0)) =? (+3)) (true)
15 | :test ((pair-ternary (+2) (+1)) =? (+7)) (true)
16 |
17 | # strong Rosenberg unpairing function
18 | unpair-ternary [[[go] (1 - (0 ⋅ 0))] (sqrt 0)] ⧗ Number → (Pair Number Number)
19 | go (0 1) (0 : 1) (1 : (1 ⋅ 1 + 1 + 1 - 2))
20 |
21 | :test (strip <$> (unpair-ternary (+0))) ((+0) : (+0))
22 | :test (strip <$> (unpair-ternary (+1))) ((+0) : (+1))
23 | :test (strip <$> (unpair-ternary (+3))) ((+1) : (+0))
24 | :test (strip <$> (unpair-ternary (+7))) ((+2) : (+1))
25 |
--------------------------------------------------------------------------------
/docs/samples.template:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | bruijn samples
8 |
9 |
10 |
bruijn samples
11 |
These are some code samples written in the bruijn programming language. Most of the examples are copies of our solutions on Rosetta Code. All examples are written by bruijn's contributors and may be used according to the GNU FDL license.
12 |
While solutions to the Project Euler puzzles should not be published in general, we may provide bruijn's solutions to the first 100 puzzles for introductory purposes. This is tolerated by the creators.
13 |
14 | LINKS
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/samples/test_all.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | if [ -z "$1" ]; then
4 | echo "Usage: $0 "
5 | exit 1
6 | fi
7 |
8 | # =================
9 | # Euler/Fun/Rosetta
10 | # =================
11 |
12 | echo "# useful for run the tests of all samples at once" >All.bruijn
13 | echo >>All.bruijn
14 |
15 | FILES="$(find euler fun rosetta -type f -name "*.bruijn")"
16 |
17 | for f in $FILES; do
18 | echo ":import ${f%*.bruijn} ." >>All.bruijn
19 | done
20 |
21 | # for ci, just run `bruijn All.bruijn`
22 | echo >>All.bruijn
23 | echo "main [[0]]" >>All.bruijn
24 |
25 | if cat /dev/null | bruijn All.bruijn -r "$1" | tee /dev/fd/2 | grep -q "ERROR"; then
26 | exit 1
27 | fi
28 |
29 | # ===
30 | # AOC
31 | # ===
32 |
33 | FILES="$(find aoc -type f -name "*.bruijn")"
34 |
35 | for f in $FILES; do
36 | dir="$(dirname "$f")"
37 | cat "$dir/input" | bruijn -r "$1" "$f" | tail -n1 >temp.out
38 | cmp temp.out "$dir/output.check" || (
39 | echo "AOC check $f failed"
40 | exit 1
41 | )
42 | done
43 |
44 | rm -f temp.out
45 |
--------------------------------------------------------------------------------
/samples/fun/encode.bruijn:
--------------------------------------------------------------------------------
1 | # encode lambda terms as numbers
2 | # from https://hbr.github.io/Lambda-Calculus/computability/text.html
3 | # see also std/Meta for unary variant
4 |
5 | :import std/Combinator .
6 | :import std/Logic .
7 | :import std/Math .
8 |
9 | # compute pairing function [n,m]
10 | compress [[--((pow (+2) 1) ⋅ ++((+2) ⋅ 0))]] ⧗ Number → Number → Number
11 |
12 | :test ((compress (+0) (+0)) =? (+0)) (true)
13 | :test ((compress (+1) (+0)) =? (+1)) (true)
14 | :test ((compress (+0) (+1)) =? (+2)) (true)
15 | :test ((compress (+2) (+1)) =? (+11)) (true)
16 |
17 | idx compress (+0) ⧗ Number → Number
18 |
19 | app (compress (+1)) ∘∘ compress ⧗ Number → Number → Number
20 |
21 | abs (compress (+2)) ∘∘ compress ⧗ Number → Number → Number
22 |
23 | enc_zero abs (+1) (abs (+0) (idx (+0))) ⧗ Number
24 |
25 | enc_inc abs (+2) (abs (+1) (abs (+0) enc)) ⧗ Number
26 | enc app (idx (+1)) (app (app (idx (+2)) (idx (+1))) (idx (+0)))
27 |
28 | wrap [0 (app enc_inc) enc_zero] ⧗ Number → Number
29 |
30 | main [[0]]
31 |
--------------------------------------------------------------------------------
/docs/wiki_src/technical/performance.md:
--------------------------------------------------------------------------------
1 | # Performance
2 |
3 | In general, the reduction of practical programs encoded in lambda
4 | calculus is not very efficient when compared to traditional programming
5 | languages. We do, however, work a lot on making the performance as
6 | comparable as possible:
7 |
8 | - We have different reducers and constantly benchmark and improve them
9 | in order to find the most efficient method of reduction. Read more
10 | about our [reducer choices](reduction.md).
11 | - Bruijn uses efficient data structures by default. For example, for
12 | nary numbers we use results of Torben Mogensens investigations (as
13 | described in [number/byte encodings](../coding/data-structures.md)).
14 | - Bruijn has a `-O` flag that enables abstraction of duplicated terms
15 | - The lambda calculus optimizers
16 | [BLoC](https://github.com/marvinborner/bloc) and
17 | [BLoCade](https://github.com/marvinborner/blocade) are directly
18 | integrated into bruijn and can be enabled optionally (see
19 | [compilation](../coding/compilation.md))
20 |
--------------------------------------------------------------------------------
/std/Number/Tuple.bruijn:
--------------------------------------------------------------------------------
1 | # MIT License, Copyright (c) 2025 Marvin Borner
2 | # by Tromp @ https://github.com/tromp/AIT/blob/master/numerals/tuple_numerals.lam
3 |
4 | :import std/Tuples .
5 | :import std/Combinator .
6 | :import std/Logic/Binary .
7 |
8 | zero i
9 |
10 | inc [[1 (0 :)]]
11 |
12 | ++‣ inc
13 |
14 | :test (++zero) (t)
15 |
16 | dec [[1 0 [0]]]
17 |
18 | --‣ dec
19 |
20 | :test (--(++zero)) (zero)
21 | :test (--(++(++zero))) (t)
22 |
23 | lt? [(k false) : (0 (k true))]
24 |
25 | …… lt?
26 |
27 | :test (zero ++zero) (true)
28 | :test (++zero ++zero) (false)
29 | :test (++zero zero) (false)
30 |
31 | eq? [((k true) : false : true) : (0 (k false))]
32 |
33 | …=?… eq?
34 |
35 | :test (zero =? ++zero) (false)
36 | :test (++zero =? ++zero) (true)
37 | :test (++zero =? zero) (false)
38 |
39 | add b
40 |
41 | …+… add
42 |
43 | :test (zero + zero) (zero)
44 | :test (zero + ++zero) (++zero)
45 | :test (++zero + ++zero) (++(++zero))
46 |
47 | sub [[(0 i) ∘ 1]]
48 |
49 | …-… sub
50 |
51 | :test (zero - zero) (zero)
52 | :test (++zero - zero) (++zero)
53 | :test (++zero - ++zero) (zero)
54 |
--------------------------------------------------------------------------------
/samples/rosetta/validate_international_securities_identification_number.bruijn:
--------------------------------------------------------------------------------
1 | :import luhn_test_of_credit_card_numbers .
2 |
3 | :import std/Number/Conversion .
4 | :import std/Combinator .
5 | :import std/String .
6 | :import std/Char .
7 | :import std/Logic .
8 | :import std/Number .
9 |
10 | # verifies ISIN format
11 | format? [len ⋀? country ⋀? security ⋀? checksum]
12 | len (length 0) =? (+12)
13 | country all? uppercase? (take (+2) 0)
14 | security all? (φ or? uppercase? numeric?) (take (+9) (drop (+2) 0))
15 | checksum numeric? _0
16 |
17 | # performs luhn test
18 | checksum? (map (from-base36 → number→string)) → concat → string→number → luhn
19 | from-base36 binary→ternary → [(0 - (0 ≥? (+65) ((+65) - (+10)) (+48)))]
20 |
21 | # performs format and checksum test
22 | validate φ and? format? checksum?
23 |
24 | :test (validate "US0378331005") (true)
25 | :test (validate "US0373831005") (false)
26 | :test (validate "U50378331005") (false)
27 | :test (validate "US03378331005") (false)
28 | :test (validate "AU0000XVGZA3") (true)
29 | :test (validate "AU0000VXGZA3") (true)
30 | :test (validate "FR0000988040") (true)
31 |
--------------------------------------------------------------------------------
/license:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Marvin Borner
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/docs/wiki_src/coding/IO.md:
--------------------------------------------------------------------------------
1 | # IO
2 |
3 | Bruijn supports a variant of John Tromp's monadic IO[^1].
4 |
5 | Every program's `main`{.bruijn} function has an additional abstraction
6 | that gets applied with a lazy list of input bytes. These bytes are
7 | encoded as the syntactic sugar encoding of binary numbers, which can be
8 | manipulated with [`std/Number/Binary`](/std/Number_Binary.bruijn.html).
9 |
10 | You can use [`std/Monad`](/std/Monad.bruijn.html) to interact with the
11 | input monadically, or simply use [`std/List`](/std/List.bruijn.html)
12 | operations to work with the input as a normal list.
13 |
14 | See [data structures](data-structures.md) to learn more about lists and
15 | numbers.
16 |
17 | If you want your main function to ignore the input, just add an
18 | additional (unbound) abstraction to your definition.
19 |
20 | ## Example
21 |
22 | ``` bruijn
23 | :import std/List .
24 |
25 | # reverse the input list
26 | main [<~>0]
27 | ```
28 |
29 | ``` bash
30 | $ printf "tacocat" | bruijn reverse.bruijn
31 | tacocat
32 | ```
33 |
34 | [^1]: [Tromp, John. "Functional Bits: Lambda Calculus based Algorithmic
35 | Information Theory." (2023).](https://tromp.github.io/cl/LC.pdf)
36 |
--------------------------------------------------------------------------------
/std/List/Parigot.bruijn:
--------------------------------------------------------------------------------
1 | # MIT License, Copyright (c) 2024 Marvin Borner
2 | # see "on the representation of data in lambda-calculus" (Parigot 1989)
3 | # constant append/snoc
4 |
5 | # empty list element
6 | empty [0] ⧗ (Parigot a)
7 |
8 | # prepends an element to a list
9 | cons [[[[0 3 (2 1)]]]] ⧗ a → (Parigot a) → (Parigot a)
10 |
11 | …:… cons
12 |
13 | # returns the head of a list or k
14 | head [0 [0] [[1]]] ⧗ (Parigot a) → a
15 |
16 | :test (head ('a' : ('b' : ('c' : empty)))) ('a')
17 | :test (head empty) ([[1]])
18 |
19 | # returns the tail of a list or &ki
20 | tail [[1 0 [[0]]]] ⧗ (Parigot a) → (Parigot a)
21 |
22 | :test (tail ('a' : ('b' : ('c' : empty)))) ('b' : ('c' : empty))
23 | :test (tail empty) ([0 [[0]]])
24 |
25 | # appends two lists
26 | append [[[2 (1 0)]]] ⧗ (Parigot a) → (Parigot a) → (Parigot a)
27 |
28 | …++… append
29 |
30 | :test (append ('a' : ('b' : empty)) ('c' : ('d' : empty))) ('a' : ('b' : ('c' : ('d' : empty))))
31 |
32 | # appends an element to a list
33 | snoc [[[2 [0 2 1]]]] ⧗ (Parigot a) → a → (Parigot a)
34 |
35 | …;… snoc
36 |
37 | :test (snoc ('a' : ('b' : empty)) 'c') ('a' : ('b' : ('c' : empty)))
38 |
39 | iter [[[0 ι ρ ρ]]]
40 | ρ [[3 (1 0 0)]]
41 | ι [[4]]
42 |
--------------------------------------------------------------------------------
/std/Map.bruijn:
--------------------------------------------------------------------------------
1 | # MIT License, Copyright (c) 2024 Marvin Borner
2 | # Generic map implementation using AVL trees
3 | # the key-value pair is stored in the tree as a Church pair
4 | # some functions require a hash function!
5 | # TODO: what about hash collisions??
6 |
7 | :import std/Tree/Balanced T
8 | :import std/Option O
9 | :import std/Number N
10 | :import std/Combinator .
11 | :import std/List .
12 |
13 | >‣ &[[[[[N.compare-case 4 3 2 ^1 ^0]]]]] ⧗ (Compare Number)
14 |
15 | # key to element (for searching)
16 | ↑‣ [0 : i] ⧗ k → (Pair k v)
17 |
18 | # empty map
19 | empty T.empty ⧗ (Map k v)
20 |
21 | # returns true if a value is in a map
22 | has? [[>T.has? ↑(1 0)]] ⧗ (k → Number) → k → (Map k v) → Boolean
23 |
24 | # counts the key-value pairs in a map
25 | size T.size ⧗ (Map k v) → Number
26 |
27 | # returns the value of a key (or none)
28 | lookup (O.map &ki) ∘∘∘ [[>T.find ↑(1 0)]] ⧗ (k → Number) → k → (Map k v) → (Option v)
29 |
30 | # inserts (or replaces) a key with a value in a map
31 | insert [[[>T.insert ((2 1) : 0)]]] ⧗ (k → Number) → k → v → (Map k v) → (Map k v)
32 |
33 | :test (has? i (+2) (insert i (+2) "two" empty)) ([[1]])
34 | :test (lookup i (+2) (insert i (+2) "two" empty)) (O.some "two")
35 |
--------------------------------------------------------------------------------
/std/Box.bruijn:
--------------------------------------------------------------------------------
1 | # MIT License, Copyright (c) 2023 Marvin Borner
2 | # a box can store a single item very efficiently
3 | # similar structure to std/Option
4 |
5 | :import std/Logic .
6 | :import std/Combinator .
7 |
8 | # a empty box
9 | empty true ⧗ (Box a)
10 |
11 | # returns true if the box is empty
12 | empty? [0 true [false]] ⧗ (Box a) → Boolean
13 |
14 | ∅?‣ empty?
15 |
16 | :test (∅?empty) (true)
17 |
18 | # builds a box out of a value
19 | box [[[0 2]]] ⧗ a → (Box a)
20 |
21 | <>‣ box
22 |
23 | # returns true if the box is set
24 | set? [0 false [true]] ⧗ (Box a) → Boolean
25 |
26 | :test (set? <>[0]) (true)
27 | :test (set? empty) (false)
28 |
29 | # sets the value of a empty box, ignores argument if already set
30 | store! [[∅?1 <>0 1]] ⧗ (Box a) → a → (Box a)
31 |
32 | :test (store! <>[[0]] [[1]]) (<>[[0]])
33 | :test (store! empty [[1]]) (<>[[1]])
34 |
35 | # sets/overrides the value of a box
36 | set! [[<>0]] ⧗ (Box a) → a → (Box a)
37 |
38 | :test (set! <>[[0]] [[1]]) (<>[[1]])
39 | :test (set! empty [[1]]) (<>[[1]])
40 |
41 | # extracts value from a box or returns first argument if none
42 | get [[0 1 i]] ⧗ a → (Box b) → c
43 |
44 | :test (get [[0]] <>[[1]]) ([[1]])
45 | :test (get [[0]] empty) ([[0]])
46 |
--------------------------------------------------------------------------------
/std/Number/Bruijn.bruijn:
--------------------------------------------------------------------------------
1 | # MIT License, Copyright (c) 2023 Marvin Borner
2 | # de Bruijn numeral system (named by me)
3 | # proof that this numeral system does not support zero/eq/sub/etc. is in
4 | # Wadsworth, Christopher. "Some unusual λ-calculus numeral systems."
5 | # very sad indeed
6 |
7 | zero (+0d)
8 |
9 | # increments de Bruijn numeral
10 | inc [[[2 1]]] ⧗ Bruijn → Bruijn
11 |
12 | ++‣ inc
13 |
14 | :test (++(+0d)) ((+1d))
15 | :test (++(+5d)) ((+6d))
16 |
17 | # decrements de Bruijn numeral
18 | dec [[1 0 0]] ⧗ Bruijn → Bruijn
19 |
20 | --‣ dec
21 |
22 | :test (--(+1d)) ((+0d))
23 | :test (--(+5d)) ((+4d))
24 |
25 | # adds de Bruijn numerals
26 | add [[[2 (1 0)]]] ⧗ Bruijn → Bruijn → Bruijn
27 |
28 | …+… add
29 |
30 | :test ((+5d) + (+0d)) ((+5d))
31 | :test ((+0d) + (+5d)) ((+5d))
32 | :test ((+5d) + (+2d)) ((+7d))
33 |
34 | # multiplies de Bruijn numeral with unary number
35 | mul [[1 0]] ⧗ Unary → Bruijn → Bruijn
36 |
37 | …⋅… mul
38 |
39 | :test ((+5u) ⋅ (+5d)) ((+25d))
40 | :test ((+0u) ⋅ (+5d)) ((+0d))
41 | :test ((+5u) ⋅ (+0d)) ((+0d))
42 |
43 | # factorial function from unary number
44 | fac [0 [[0 (1 [[2 1 (1 0)]])]] [(+1d)] [0]] ⧗ Unary → Bruijn
45 |
46 | # A000217
47 | triangular [[1 [[0 (1 ([[[2 1]]] 0))]] [1] [0]]] ⧗ Unary → Bruijn
48 |
--------------------------------------------------------------------------------
/.github/workflows/gen.yml:
--------------------------------------------------------------------------------
1 | name: Deploy to pages
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | permissions:
9 | contents: read
10 | pages: write
11 | id-token: write
12 |
13 | concurrency:
14 | group: "pages"
15 | cancel-in-progress: false
16 |
17 | jobs:
18 | build:
19 | runs-on: ubuntu-latest
20 | steps:
21 | - uses: actions/checkout@v4
22 | - uses: actions/setup-python@v4
23 | - name: Setup Pages
24 | id: pages
25 | uses: actions/configure-pages@v3
26 | - name: Install dependencies
27 | run: pip3 install mkdocs-terminal
28 | - name: Generate wiki
29 | run: cd docs/ && mkdocs build
30 | - name: Generate std
31 | run: cd docs/ && ./gen.sh
32 | - name: Generate std map
33 | run: |
34 | sudo apt install python3
35 | cd std/ && ./generate_map.py >../docs/std_map.json
36 | - name: Upload artifact
37 | uses: actions/upload-pages-artifact@v3
38 | with:
39 | path: docs/
40 |
41 | deploy:
42 | environment:
43 | name: github-pages
44 | url: ${{ steps.deployment.outputs.page_url }}
45 | runs-on: ubuntu-latest
46 | needs: build
47 | steps:
48 | - name: Deploy to GitHub Pages
49 | id: deployment
50 | uses: actions/deploy-pages@v4
51 |
--------------------------------------------------------------------------------
/docs/wiki_src/custom.css:
--------------------------------------------------------------------------------
1 | pre {
2 | width: 100%;
3 | white-space: pre;
4 | overflow: auto !important;
5 | display: grid;
6 | }
7 |
8 | code {
9 | overflow: auto !important;
10 | }
11 |
12 | /* to match std */
13 | pre, pre > code, code.bruijn {
14 | background-color: #222222;
15 | }
16 |
17 | pre a {
18 | color: var(--font-color);
19 | }
20 |
21 | .headerlink {
22 | display: none;
23 | }
24 |
25 | h2,h3,h4,h5,h6 {
26 | line-height: var(--global-line-height);
27 | margin-bottom: 0.5em;
28 | }
29 |
30 | .terminal h1 {
31 | font-size: 2.33em;
32 | }
33 |
34 | .terminal h1 > * {
35 | font-size: inherit;
36 | }
37 |
38 | .terminal h2 {
39 | font-size: 1.83em;
40 | }
41 |
42 | .terminal h2 > * {
43 | font-size: inherit;
44 | }
45 |
46 | .terminal h3{
47 | font-size: 1.50em;
48 | }
49 |
50 | .terminal h3 > * {
51 | font-size: inherit;
52 | }
53 |
54 | .terminal h4 {
55 | font-size: 1.33em;
56 | }
57 |
58 | .terminal h4 > * {
59 | font-size: inherit;
60 | }
61 |
62 | .terminal h5 {
63 | font-size: 1.16em;
64 | }
65 |
66 | .terminal h5 > * {
67 | font-size: inherit;
68 | }
69 |
70 | .terminal h6 {
71 | font-size: 1em;
72 | }
73 |
74 | .terminal h6 > * {
75 | font-size: inherit;
76 | }
77 |
78 | pre:has(.language-code-showcase), .language-code-showcase {
79 | background-color: transparent;
80 | border: 0;
81 | margin: 0 auto;
82 | width: fit-content;
83 | max-width: 100%;
84 | white-space: pre;
85 | }
86 |
--------------------------------------------------------------------------------
/std/Monad/List.bruijn:
--------------------------------------------------------------------------------
1 | # MIT License, Copyright (c) 2023 Marvin Borner
2 | # monadic interface for anything based on lists (e.g. IO, strings)
3 | # afaik originally proposed by John Tromp and inspired by Haskell
4 |
5 | :import std/List .
6 | :import std/Combinator .
7 |
8 | read [0] ⧗ a → (M a)
9 |
10 | return [[1 : 0]] ⧗ a → (M a)
11 |
12 | pure return ⧗ a → (M a)
13 |
14 | # monadic bind operator
15 | bind [[[2 0 1]]] ⧗ (M a) → (a → (M b)) → (M a)
16 |
17 | …>>=… bind
18 |
19 | :test ((read >>= return) "woa") ("woa")
20 |
21 | # monadic reverse bind operator
22 | …=<<… \…>>=… ⧗ (a → (M b)) → (M a) → (M b)
23 |
24 | :test ((return =<< read) "woa") ("woa")
25 |
26 | # monadic compose operator
27 | …>>… [[1 >>= [1]]] ⧗ (M a) → (M b) → (M b)
28 |
29 | :test ((read >> (return 'a')) "hah") ("aah")
30 |
31 | # monadifies a list
32 | lift-m map ⧗ (a → b) → (M a) → (M b)
33 |
34 | # monadifies a list with two monadic arguments
35 | lift-m2 [[[concat ([[4 1 0] <$> 1] <$> 1)]]] ⧗ (a → b → c) → (M a) → (M b) → (M c)
36 |
37 | # evaluates monadic actions
38 | sequence foldr (lift-m2 cons) {}empty ⧗ (List (M a)) → (M (List a))
39 |
40 | >‣ [sequence ∘∘ 0]
41 |
42 | # traverses list based on modifier
43 | traverse sequence ∘∘ map ⧗ (a → (M b)) → (N a) → (M (N b))
44 |
45 | # performs action n times
46 | replicate-m >replicate ⧗ Number → (M a) → (M (List a))
47 |
48 | # maps elements to a monadic action
49 | map-m >map ⧗ (a → (M b)) → (List a) → (M (List b))
50 |
--------------------------------------------------------------------------------
/broogle.sh:
--------------------------------------------------------------------------------
1 | #!/bin/env bash
2 | # MIT License, Copyright (c) 2023 Marvin Borner
3 | # TODO: jq is quite slow tbh hmm
4 |
5 | # TODO: Custom path and absolute library path
6 | path="std/"
7 |
8 | pretty() {
9 | jq -c 'select(.type == "match") | .data' <<<"$1" |
10 | while IFS=$"\n" read -r c; do
11 | read file line name type < <(echo $(jq -r '.path.text, .line_number, (.lines.text | split(" ")[0]), (.lines.text | split("⧗")[1] | gsub("^\\s+|\\s+$";""))' <<<"$c" 2>/dev/null))
12 | desc=$(sed -n "$((line > 10 ? line - 10 : 1)),${line}p" "$file" | awk -v RS= 'END{print}' | awk '/^# /')
13 | alia=$(sed -n "$line,$ p" "$file" | sed -n "s/^\\(.*\\) $name$/\\1/p" | xargs)
14 | [ -z "$type" ] && type="Any"
15 |
16 | echo -e "\e[101m\e[30mFOUND\e[0m \e[95m$name\e[0m ⧗ \e[33m$type\e[0m"
17 | [ -z "$alia" ] || echo -e "\e[106m\e[30malso\e[0m \e[95m$alia\e[0m"
18 | echo -e "\e[104m\e[30min\e[0m $file:$line"
19 | [ -z "$desc" ] || echo -e "\e[3;2m$desc\e[0m"
20 | echo
21 | done
22 | }
23 |
24 | search_type() {
25 | json=$(rg --json "^.* .* ⧗ $1$" "$path")
26 | pretty "$json"
27 | }
28 |
29 | search_name() {
30 | json=$(rg --json "^$1 .*$" "$path")
31 | pretty "$json"
32 | }
33 |
34 | # TODO: Add search by comment
35 |
36 | case $1 in
37 | -t)
38 | shift
39 | search_type "${@//->/→}"
40 | ;;
41 | -f)
42 | shift
43 | search_name "$@"
44 | ;;
45 | -c)
46 | echo "sorry, not supported right now"
47 | ;;
48 | *)
49 | echo "unknown command $1, please use -t/f/c"
50 | ;;
51 | esac
52 |
--------------------------------------------------------------------------------
/samples/rosetta/special_factorials.bruijn:
--------------------------------------------------------------------------------
1 | :import std/Combinator .
2 | :import std/List .
3 | :import std/Math .
4 |
5 | factorial [∏ (+1) → 0 | [0]]
6 |
7 | superfactorial [∏ (+1) → 0 | factorial]
8 |
9 | hyperfactorial [∏ (+1) → 0 | [0 ** 0]]
10 |
11 | alternating-factorial y [[=?0 0 ((factorial 0) - (1 --0))]]
12 |
13 | exponential-factorial y [[=?0 0 (0 ** (1 --0))]]
14 |
15 | :test ((factorial (+4)) =? (+24)) ([[1]])
16 | :test ((superfactorial (+4)) =? (+288)) ([[1]])
17 | :test ((hyperfactorial (+4)) =? (+27648)) ([[1]])
18 | :test ((alternating-factorial (+3)) =? (+5)) ([[1]])
19 | :test ((exponential-factorial (+4)) =? (+262144)) ([[1]])
20 |
21 | invfac y [[[compare-case 1 (2 ++1 0) (-1) 0 (∏ (+0) → --1 | ++‣)]]] (+0)
22 |
23 | :test ((invfac (+1)) =? (+0)) ([[1]])
24 | :test ((invfac (+2)) =? (+2)) ([[1]])
25 | :test ((invfac (+6)) =? (+3)) ([[1]])
26 | :test ((invfac (+24)) =? (+4)) ([[1]])
27 | :test ((invfac (+120)) =? (+5)) ([[1]])
28 | :test ((invfac (+720)) =? (+6)) ([[1]])
29 | :test ((invfac (+5040)) =? (+7)) ([[1]])
30 | :test ((invfac (+40320)) =? (+8)) ([[1]])
31 | :test ((invfac (+362880)) =? (+9)) ([[1]])
32 | :test ((invfac (+3628800)) =? (+10)) ([[1]])
33 | :test ((invfac (+119)) =? (-1)) ([[1]])
34 |
35 | seq-a [((superfactorial 0) : ((hyperfactorial 0) : {}(alternating-factorial 0)))] <$> (iterate ++‣ (+0))
36 |
37 | seq-b exponential-factorial <$> (iterate ++‣ (+0))
38 |
39 | # return first 10/4 elements of both sequences
40 | main [(take (+10) seq-a) : {}(take (+5) seq-b)]
41 |
--------------------------------------------------------------------------------
/docs/genstd.sh:
--------------------------------------------------------------------------------
1 | #!/bin/env bash
2 |
3 | set -e
4 |
5 | rm -rf std/ && mkdir -p std/
6 |
7 | files=$(find ../std/ -type f -name "*.bruijn" -printf '%h\0%d\0%p\n' | sort -t '\0' -n | awk -F '\0' '{print $3}')
8 | links=""
9 |
10 | tot_ntests=0
11 | tot_ndefs=0
12 | prefix="seeelefant"
13 | for file in $files; do
14 | name=$(cut -c8- <<<"$file")
15 | _prefix=$(cut -c8- <<<"$file" | sed -rn 's@^(.*)/.*$@\1@p')
16 | if ! [ "$prefix" = "$_prefix" ]; then
17 | prefix=$_prefix
18 | links="$links\n