├── .github ├── FUNDING.yml └── workflows │ └── ci.yaml ├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build.zig ├── build.zig.zon ├── buzz.code-workspace ├── example.png ├── examples ├── 2048.buzz ├── fibonacci.buzz ├── fizzbuzz.buzz ├── game-of-life.buzz ├── sdl-wrapped.buzz ├── sdl.buzz ├── sqlite.buzz └── voronoi-diagram.buzz ├── logo.png ├── package-lock.json ├── package.json ├── src ├── Ast.zig ├── Chunk.zig ├── Codegen.zig ├── FFI.zig ├── Jit.zig ├── Parser.zig ├── Reporter.zig ├── Scanner.zig ├── StringParser.zig ├── Token.zig ├── behavior.zig ├── builtin.zig ├── builtin │ ├── fiber.zig │ ├── list.zig │ ├── map.zig │ ├── pattern.zig │ ├── range.zig │ └── str.zig ├── buzz_api.zig ├── disassembler.zig ├── io.zig ├── jit_extern_api.zig ├── jmp.zig ├── lib │ ├── buffer.buzz │ ├── buzz_api.zig │ ├── buzz_buffer.zig │ ├── buzz_crypto.zig │ ├── buzz_debug.zig │ ├── buzz_ffi.zig │ ├── buzz_fs.zig │ ├── buzz_gc.zig │ ├── buzz_http.zig │ ├── buzz_io.zig │ ├── buzz_math.zig │ ├── buzz_os.zig │ ├── buzz_serialize.zig │ ├── buzz_std.zig │ ├── crypto.buzz │ ├── debug.buzz │ ├── errors.buzz │ ├── ffi.buzz │ ├── fs.buzz │ ├── gc.buzz │ ├── http.buzz │ ├── io.buzz │ ├── io.zig │ ├── jmp.zig │ ├── math.buzz │ ├── mimalloc.zig │ ├── os.buzz │ ├── serialize.buzz │ ├── std.buzz │ ├── testing.buzz │ └── wasm.zig ├── linenoise.zig ├── lsp.zig ├── main.zig ├── memory.zig ├── mimalloc.zig ├── mir.zig ├── obj.zig ├── pcre.zig ├── renderer.zig ├── repl.html ├── repl.zig ├── tests │ └── fmt.zig ├── value.zig ├── vm.zig ├── wasm.ts ├── wasm.zig ├── wasm_repl.zig └── zigtypes.zig └── tests ├── 001-basic-types.buzz ├── 002-operators.buzz ├── 003-control-flow.buzz ├── 004-lists.buzz ├── 005-maps.buzz ├── 006-enums.buzz ├── 007-objects.buzz ├── 008-inline-catch.buzz ├── 009-gc.buzz ├── 010-placeholder-cycle.buzz ├── 011-list-map-properties.buzz ├── 012-lambda.buzz ├── 013-import-export.buzz ├── 014-import-lib.buzz ├── 015-interpolation.buzz ├── 016-optionals.buzz ├── 017-for.buzz ├── 018-foreach.buzz ├── 019-is.buzz ├── 021-upvalues.buzz ├── 022-io.buzz ├── 023-std.buzz ├── 024-os.buzz ├── 025-fs.buzz ├── 026-break-continue.buzz ├── 027-run-file.buzz ├── 028-math.buzz ├── 029-default-arguments.buzz ├── 030-str.buzz ├── 031-json.buzz ├── 032-debug.buzz ├── 033-invoke.buzz ├── 034-scope.buzz ├── 035-const-expr.buzz ├── 036-pattern.buzz ├── 037-dead-branches.buzz ├── 038-fibers.buzz ├── 039-buffer.buzz ├── 040-bitwise.buzz ├── 041-iterator.buzz ├── 042-anonymous-objects.buzz ├── 044-break-continue.buzz ├── 045-mutual-import.buzz ├── 046-try-catch.buzz ├── 047-if-arrow.buzz ├── 048-generics.buzz ├── 049-multiline-strings.buzz ├── 050-protocols.buzz ├── 051-functional.buzz ├── 052-inline-if.buzz ├── 053-range.buzz ├── 056-crypto.buzz ├── 057-any.buzz ├── 058-ffi.buzz ├── 059-types-as-value.buzz ├── 060-free-identifiers.buzz ├── 061-utf8.buzz ├── 062-discarded-value.buzz ├── 063-nullable-default.buzz ├── 064-throw-inside-try.buzz ├── 065-inferred-var-type.buzz ├── 066-object-generics.buzz ├── 068-testing.buzz ├── 069-named-expr.buzz ├── 070-block-expression.buzz ├── 071-tail-call.buzz ├── 072-labels.buzz ├── 073-tuples.buzz ├── 074-checked-subscript.buzz ├── 075-composite-assign.buzz ├── bench ├── 001-btree.buzz ├── 002-merkle.buzz ├── 003-nbody.buzz ├── 004-spectral.buzz ├── 005-k-nucleoide.buzz ├── 006-fasta.buzz ├── 007-fib.buzz ├── 008-for.buzz ├── 009-grid.buzz ├── 010-ackermann.buzz ├── 011-bubble-sort.buzz ├── README.md └── reference │ ├── btree.lua │ ├── fasta-output.txt │ ├── fasta.dart │ ├── fasta.lua │ ├── grid.lua │ ├── k-nucleoide.lua │ ├── knucleotide-input.txt │ └── knucleotide-output.txt ├── compile_errors ├── 001-const.buzz ├── 002-break.buzz ├── 004-not-callable.buzz ├── 005-bad-named-variable-value.buzz ├── 006-deep-yield.buzz ├── 007-yield-location.buzz ├── 008-error-message.buzz ├── 009-bad-named-variable-value.buzz ├── 010-multiple-location.buzz ├── 011-protocol.buzz ├── 012-collect-signature.buzz ├── 013-tostring-signature.buzz ├── 014-main-signature.buzz ├── 015-bad-pattern.buzz ├── 016-unused-local.buzz ├── 017-invalid-int-literal.buzz ├── 018-invalid-float-literal.buzz ├── 019-invalid-int-hex-literal.buzz ├── 020-invalid-int-bin-literal.buzz ├── 021-fiber-error-location.buzz ├── 022-var-decl-without-value.buzz ├── 023-empty-import.buzz ├── 024-misplaced-out.buzz ├── 025-multiple-out.buzz ├── 026-out-last-statement.buzz ├── 027-early-return.buzz ├── 028-unused-import.buzz ├── 029-label-not-found.buzz ├── 030-final-objects.buzz ├── 031-long-tuple.buzz ├── 032-tuple-mix.buzz ├── 033-immutable-list.buzz ├── 034-var-not-assigned.buzz └── utils │ └── bad-import.buzz ├── manual ├── 001-cli-args.buzz ├── 002-error.buzz ├── 003-io.buzz ├── 004-os.buzz ├── 005-tcp-client.buzz ├── 006-http-client.buzz └── 007-fd-poller.buzz └── utils ├── foreign.zig ├── import-a.buzz ├── import-b.buzz └── testing.buzz /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [giann] 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # zig 2 | zig-cache/ 3 | \.zig-cache/ 4 | zig-out/ 5 | zig-test/ 6 | dist/ 7 | 8 | .vscode/ 9 | *.bc 10 | *.s 11 | *.dylib 12 | *.so 13 | *.o 14 | 15 | tests/utils/libforeign.* 16 | buzz_history 17 | 18 | node_modules 19 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "mir"] 2 | path = vendors/mir 3 | url = https://github.com/vnmakarov/mir 4 | [submodule "vendors/pcre2"] 5 | path = vendors/pcre2 6 | url = https://github.com/PCRE2Project/pcre2 7 | [submodule "vendors/mimalloc"] 8 | path = vendors/mimalloc 9 | url = https://github.com/microsoft/mimalloc.git 10 | [submodule "vendors/linenoise"] 11 | path = vendors/linenoise 12 | url = https://github.com/antirez/linenoise.git 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright © 2021-present Benoit Giannangeli 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | buzz 3 |

4 | 5 | # 👨‍🚀 buzz 6 | 7 | A small/lightweight statically typed scripting language written in Zig 8 | 9 |

10 | buzz code example 11 |

12 | 13 |

14 | HomepageDiscord 15 |

16 | 17 | ## Features 18 | 19 | - Small in size and complexity (just a bit more than Lua though) 20 | - Statically typed 21 | - Unambiguous 22 | - No nonsense coercion 23 | - [Fibers](#fibers) 24 | - JIT compilation with [MIR](https://github.com/vnmakarov/mir) 25 | - Tooling 26 | - [VS Code extension](https://marketplace.visualstudio.com/items?itemName=giann.buzz) 27 | - Helix support thanks to [tree-sitter grammar](https://github.com/buzz-language/tree-sitter-buzz) 28 | - LSP 29 | - Formatter 30 | 31 | ## How to build and install 32 | 33 | _Latest zig version supported: 0.15.0-dev.263+d8153fa74_ 34 | 35 | ### Requirements 36 | - Since this is built with Zig, you should be able to build buzz on a wide variety of architectures even though this has only been tested on x86/M1. 37 | - Linux or macOS (Windows support [is coming](https://github.com/buzz-language/buzz/issues/74)) 38 | - libc 39 | - zig master 40 | 41 | ### Build 42 | 1. Clone the project: `git clone https://github.com/buzz-language/buzz ` 43 | 2. Checkout submodules: `git submodule update --init` 44 | 3. Have fun: `zig build run -- ` to run a script or `zig build run` to start the REPL 45 | 46 | ### Install 47 | 48 | ```bash 49 | # install locally at ~/.local 50 | zig build -Doptimize=ReleaseSafe install -p ~/.local 51 | 52 | # install globally at /usr/local 53 | sudo zig build -Doptimize=ReleaseSafe install -p /usr/local 54 | ``` 55 | 56 | If you're usage if performance critical (game dev for example), you can build using `-Doptimize=ReleaseFast`. 57 | 58 | Remember to modify PATH to include the `bin` directory where it is installed. For example, `export PATH=PATH:/home/xxx/.local/bin`. You can then run buzz with `buzz `. Or you can simply run `buzz` to start the REPL. 59 | -------------------------------------------------------------------------------- /build.zig.zon: -------------------------------------------------------------------------------- 1 | .{ 2 | .name = .buzz, 3 | .fingerprint = 0x87514371589e863a, 4 | .version = "0.6.0", 5 | .minimum_zig_version = "0.15.0-dev.190+bfbf4badd", 6 | .dependencies = .{ 7 | .clap = .{ 8 | .url = "git+https://github.com/Hejsil/zig-clap.git#e47028deaefc2fb396d3d9e9f7bd776ae0b2a43a", 9 | .hash = "clap-0.10.0-oBajB434AQBDh-Ei3YtoKIRxZacVPF1iSwp3IX_ZB8f0", 10 | }, 11 | .zig_lsp_kit = .{ 12 | .url = "git+https://github.com/buzz-language/zig-lsp-kit.git#fe16f27f41ca5ad7ffb4e3d73e9e2a5ed42b0cc9", 13 | .hash = "zig_lsp_kit-0.0.0-SaK5ApS9AACj10m7vj2CNyiD1k8LdA5jmKMAaEwQ71RY", 14 | }, 15 | }, 16 | .paths = .{""}, 17 | } 18 | -------------------------------------------------------------------------------- /buzz.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": { 8 | "files.associations": { 9 | "string.h": "c", 10 | "common.h": "c", 11 | "compiler.h": "c", 12 | "debug.h": "c", 13 | "object.h": "c", 14 | "memory.h": "c", 15 | "vm.h": "c", 16 | "iterator": "c", 17 | "memory": "c", 18 | "__availability": "c", 19 | "complex": "c", 20 | "__bit_reference": "c", 21 | "__hash_table": "c", 22 | "__split_buffer": "c", 23 | "array": "c", 24 | "string": "c", 25 | "string_view": "c", 26 | "unordered_map": "c", 27 | "vector": "c", 28 | "algorithm": "c", 29 | "__functional_base": "c", 30 | "__node_handle": "c", 31 | "atomic": "c", 32 | "bitset": "c", 33 | "chrono": "c", 34 | "cstddef": "c", 35 | "__memory": "c", 36 | "functional": "c", 37 | "limits": "c", 38 | "locale": "c", 39 | "optional": "c", 40 | "ratio": "c", 41 | "system_error": "c", 42 | "tuple": "c", 43 | "type_traits": "c" 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buzz-language/buzz/96dfed76fbf35f86698dbbfb7464c22ad16df961/example.png -------------------------------------------------------------------------------- /examples/fibonacci.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | fun fibonacci(n: int) > void *> int? { 4 | var n1 = 0; 5 | var n2 = 1; 6 | var next: int? = null; 7 | 8 | foreach (_ in 0..n) { 9 | _ = yield n1; 10 | next = n1 + n2; 11 | n1 = n2; 12 | n2 = next!; 13 | } 14 | } 15 | 16 | fun main(args: [str]) > void { 17 | final N = std\parseInt(args[?0] ?? "10") ?? 10; 18 | 19 | foreach (n in &fibonacci(N)) { 20 | std\print("{n}"); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/fizzbuzz.buzz: -------------------------------------------------------------------------------- 1 | namespace examples\fizzbuzz; 2 | 3 | import "std"; 4 | 5 | fun fizzBuzz(n: int) > str { 6 | final fizz = n % 3 == 0; 7 | final buzz = n % 5 == 0; 8 | 9 | if (fizz and buzz) { 10 | return "FizzBuzz"; 11 | } else if (fizz) { 12 | return "Fizz"; 13 | } else if (buzz) { 14 | return "Buzz"; 15 | } 16 | 17 | return "{n}"; 18 | } 19 | 20 | fun main(args: [str]) > void { 21 | final limit = std\parseInt(args[?0] ?? "10") ?? 10; 22 | 23 | foreach (n in 0..limit) { 24 | std\print("{n}: {fizzBuzz(n)}"); 25 | } 26 | } 27 | 28 | test "FizzBuzz" { 29 | std\assert(fizzBuzz(1) == "1"); 30 | std\assert(fizzBuzz(7) == "7"); 31 | 32 | std\assert(fizzBuzz(3) == "Fizz"); 33 | std\assert(fizzBuzz(9) == "Fizz"); 34 | 35 | std\assert(fizzBuzz(5) == "Buzz"); 36 | std\assert(fizzBuzz(20) == "Buzz"); 37 | 38 | std\assert(fizzBuzz(15) == "FizzBuzz"); 39 | std\assert(fizzBuzz(45) == "FizzBuzz"); 40 | } -------------------------------------------------------------------------------- /examples/game-of-life.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | import "io"; 3 | import "examples/sdl-wrapped" as _; 4 | 5 | // buzz -L/path/to/SDL2.dylib/so/dll examples/game-of-life.buzz 6 | object World { 7 | width: int, 8 | height: int, 9 | cells: mut [bool], 10 | 11 | static fun init(width: int, height: int) > mut World { 12 | final world = mut World{ 13 | width , 14 | height, 15 | cells = mut [], 16 | }; 17 | 18 | for (i: int = 0; i < width * height; i = i + 1) { 19 | world.cells.append(std\random(max: 5) == 1); 20 | } 21 | 22 | return world; 23 | } 24 | 25 | fun neighbors(x: int, y: int) > int { 26 | var liveCount = 0; 27 | for (dy: int = y - 1; dy <= y + 1; dy = dy + 1) { 28 | for (dx: int = x - 1; dx <= x + 1; dx = dx + 1) { 29 | if (dy > 0 and dy > 0 and dx < this.width and dy < this.height and this.cells[dy * this.width + dx]) { 30 | liveCount = liveCount + 1; 31 | } 32 | } 33 | } 34 | 35 | return liveCount - if (this.cells[y * this.width + x]) 1 else 0; 36 | } 37 | 38 | mut fun step() > void { 39 | final cells: mut [bool] = mut []; 40 | for (y: int = 0; y < this.height; y = y + 1) { 41 | for (x: int = 0; x < this.width; x = x + 1) { 42 | final coordinate = y * this.width + x; 43 | final liveNeighbors = this.neighbors(x: x, y: y); 44 | 45 | cells.append(liveNeighbors == 3 or (this.cells[coordinate] and liveNeighbors == 2)); 46 | } 47 | } 48 | 49 | this.cells = cells; 50 | } 51 | 52 | fun dump() > void { 53 | for (y: int = 0; y < this.height; y = y + 1) { 54 | for (x: int = 0; x < this.width; x = x + 1) { 55 | io\stdout.write( 56 | if (this.cells[y * this.width + x]) 57 | "x" 58 | else 59 | "." 60 | ) catch void; 61 | } 62 | io\stdout.write("\n") catch void; 63 | } 64 | } 65 | 66 | fun draw(renderer: Renderer) > void !> SDLError { 67 | final cellWidth = 800 / this.width; 68 | final cellHeight = 600 / this.height; 69 | 70 | for (y: int = 0; y < this.height; y = y + 1) { 71 | for (x: int = 0; x < this.width; x = x + 1) { 72 | renderer.setRenderDrawColor( 73 | if (this.cells[y * this.width + x]) 74 | Color{ 75 | red = 0x01, 76 | green = 0x9d, 77 | blue = 0xe0, 78 | } 79 | else 80 | Color{ 81 | red = 0xff, 82 | green = 0xff, 83 | blue = 0xff, 84 | } 85 | ); 86 | 87 | renderer.fillRect( 88 | Rect{ 89 | x = cellWidth * x, 90 | y = cellHeight * y, 91 | width = cellWidth, 92 | height = cellHeight, 93 | } 94 | ); 95 | } 96 | } 97 | 98 | renderer.setRenderTarget(null); 99 | renderer.setRenderDrawColor( 100 | Color{ 101 | red = 0, 102 | green = 0, 103 | blue = 0, 104 | } 105 | ); 106 | } 107 | } 108 | 109 | fun loop(world: mut World, sdl: SDL, renderer: Renderer, texture: Texture) > void !> SDLError { 110 | var dt = sdl.ticks(); 111 | while (true) { 112 | if (sdl.pollEvent() -> event) { 113 | if (event.@"type" == EventType.Quit.value) { 114 | break; 115 | } 116 | } 117 | 118 | if (sdl.ticks() - dt > 300) { 119 | dt = sdl.ticks(); 120 | renderer.setRenderTarget(texture); 121 | world.draw(renderer); 122 | renderer.setRenderTarget(null); 123 | 124 | world.step(); 125 | } 126 | 127 | renderer.renderCopy(texture); 128 | 129 | renderer.renderPresent(); 130 | 131 | sdl.delay(100.0); 132 | } 133 | } 134 | 135 | fun main(args: [str]) > void !> SDLError { 136 | final sdl = SDL.init([Subsystem.Video]); 137 | 138 | final window = Window.init( 139 | "buzz example: Game of Life", 140 | width: 800, 141 | height: 600, 142 | flags: [WindowFlag.OpenGL, WindowFlag.AllowHighDPI], 143 | ); 144 | 145 | final renderer = window.createRenderer( 146 | index: -1, 147 | flags: [RendererFlag.Accelerated, RendererFlag.TargetTexture], 148 | ); 149 | 150 | final texture = renderer.createTexture( 151 | format: PixelFormat.RGBA8888, 152 | access: TextureAccess.Target, 153 | width: 800, 154 | height: 600, 155 | ); 156 | 157 | final width = if (args.len() > 0) std\parseInt(args[0]) else null; 158 | final height = if (args.len() > 1) std\parseInt(args[1]) else null; 159 | 160 | final world = World.init( 161 | width: width ?? 10, 162 | height: height ?? 10, 163 | ); 164 | 165 | loop( 166 | world, 167 | sdl, 168 | texture, 169 | renderer, 170 | ); 171 | 172 | sdl.quit(); 173 | } 174 | -------------------------------------------------------------------------------- /examples/sdl.buzz: -------------------------------------------------------------------------------- 1 | import "examples/sdl-wrapped" as _; 2 | 3 | // buzz -L/path/to/SDL2.dylib/so/dll examples/sdl.buzz 4 | fun main() > int !> SDLError { 5 | final sdl = SDL.init([Subsystem.Video]); 6 | 7 | final window = Window.init( 8 | "SDL FFI test", 9 | width: 800, 10 | height: 600, 11 | flags: [WindowFlag.OpenGL, WindowFlag.AllowHighDPI], 12 | ); 13 | 14 | final renderer = window.createRenderer( 15 | index: -1, 16 | flags: [RendererFlag.Accelerated, RendererFlag.TargetTexture], 17 | ); 18 | 19 | final texture = renderer.createTexture( 20 | format: PixelFormat.RGBA8888, 21 | access: TextureAccess.Target, 22 | width: 800, 23 | height: 600, 24 | ); 25 | 26 | renderer.setRenderTarget(texture); 27 | renderer.setRenderDrawColor( 28 | Color{ 29 | red = 0x01, 30 | green = 0x9d, 31 | blue = 0xe0, 32 | } 33 | ); 34 | 35 | renderer.fillRect( 36 | Rect{ 37 | x = 200, 38 | y = 200, 39 | width = 200, 40 | height = 200, 41 | } 42 | ); 43 | 44 | renderer.setRenderTarget(null); 45 | renderer.setRenderDrawColor( 46 | Color{ 47 | red = 0, 48 | green = 0, 49 | blue = 0, 50 | } 51 | ); 52 | 53 | while (true) { 54 | if (sdl.pollEvent() -> event) { 55 | if (event.@"type" == EventType.Quit.value) { 56 | break; 57 | } 58 | } 59 | 60 | renderer.renderCopy(texture); 61 | 62 | renderer.renderPresent(); 63 | 64 | sdl.delay(10.0); 65 | } 66 | 67 | sdl.quit(); 68 | 69 | return 0; 70 | } 71 | -------------------------------------------------------------------------------- /examples/voronoi-diagram.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | import "examples/sdl-wrapped" as _; 3 | import "math"; 4 | 5 | fun hypot(x: int, y: int) > int { 6 | return std\toInt( 7 | math\sqrt( 8 | std\toDouble( 9 | x * x + y * y 10 | ) 11 | ) 12 | ); 13 | } 14 | 15 | fun generateVoronoi(renderer: Renderer, width: int, height: int, numCells: int) > void !> SDLError { 16 | final nx: mut [int] = mut []; 17 | final ny: mut [int] = mut []; 18 | final colors: mut [Color] = mut []; 19 | 20 | foreach (_ in 0..numCells) { 21 | nx.append(std\random(min: 0, max: width)); 22 | ny.append(std\random(min: 0, max: height)); 23 | colors.append(Color{ 24 | red = std\random(min: 0, max: 255), 25 | green = std\random(min: 0, max: 255), 26 | blue = std\random(min: 0, max: 255) 27 | }); 28 | } 29 | 30 | renderer.setRenderDrawColor( 31 | Color{ 32 | red = 255, 33 | green = 255, 34 | blue = 255, 35 | } 36 | ); 37 | 38 | foreach (y in 0..width) { 39 | foreach (x in 0..height) { 40 | var dmin = hypot(x: width - 1, y: height - 1); 41 | var j = -1; 42 | foreach (i in 0..numCells) { 43 | final d = hypot(x: nx[i] - x, y: ny[i] - y); 44 | if (d < dmin) { 45 | dmin = d; 46 | j = i; 47 | } 48 | } 49 | 50 | renderer.setRenderDrawColor(colors[j]); 51 | 52 | renderer.drawPoint(x, y); 53 | } 54 | } 55 | 56 | renderer.setRenderDrawColor( 57 | Color{ 58 | red = 255, 59 | green = 255, 60 | blue = 255, 61 | } 62 | ); 63 | 64 | foreach (b in 0..numCells) { 65 | renderer.drawPoint(x: nx[b], y: ny[b]); 66 | } 67 | } 68 | 69 | fun main() > void !> SDLError { 70 | final width = 400; 71 | final height = 400; 72 | final numCells = 50; 73 | 74 | final sdl = SDL.init([Subsystem.Video]); 75 | 76 | final window = Window.init( 77 | "buzz example: Voronoi Diagram", 78 | width, 79 | height, 80 | flags: [WindowFlag.OpenGL, WindowFlag.AllowHighDPI], 81 | ); 82 | 83 | final renderer = window.createRenderer( 84 | index: -1, 85 | flags: [RendererFlag.Accelerated, RendererFlag.TargetTexture], 86 | ); 87 | 88 | final texture = renderer.createTexture( 89 | format: PixelFormat.RGBA8888, 90 | access: TextureAccess.Target, 91 | width, 92 | height, 93 | ); 94 | 95 | renderer.setRenderTarget(texture); 96 | generateVoronoi(renderer, width, height, numCells); 97 | renderer.setRenderTarget(null); 98 | 99 | while (true) { 100 | if (sdl.pollEvent() -> event) { 101 | if (event.@"type" == EventType.Quit.value) { 102 | break; 103 | } 104 | } 105 | 106 | renderer.renderCopy(texture); 107 | renderer.renderPresent(); 108 | 109 | sdl.delay(100.0); 110 | } 111 | 112 | sdl.quit(); 113 | } 114 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/buzz-language/buzz/96dfed76fbf35f86698dbbfb7464c22ad16df961/logo.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "type": "module", 4 | "devDependencies": { 5 | "esbuild": "^0.20.0", 6 | "typescript": "^5.3.3" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Chunk.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const Ast = @import("Ast.zig"); 3 | const Value = @import("value.zig").Value; 4 | const VM = @import("vm.zig").VM; 5 | const Token = @import("Token.zig"); 6 | 7 | pub const OpCode = enum(u8) { 8 | OP_CONSTANT, 9 | OP_NULL, 10 | OP_VOID, 11 | OP_TRUE, 12 | OP_FALSE, 13 | OP_POP, 14 | OP_COPY, 15 | OP_CLONE, 16 | 17 | OP_DEFINE_GLOBAL, 18 | OP_GET_GLOBAL, 19 | OP_SET_GLOBAL, 20 | OP_GET_LOCAL, 21 | OP_SET_LOCAL, 22 | OP_GET_UPVALUE, 23 | OP_SET_UPVALUE, 24 | OP_GET_LIST_SUBSCRIPT, 25 | OP_GET_MAP_SUBSCRIPT, 26 | OP_GET_STRING_SUBSCRIPT, 27 | OP_SET_LIST_SUBSCRIPT, 28 | OP_SET_MAP_SUBSCRIPT, 29 | 30 | OP_EQUAL, 31 | OP_IS, 32 | OP_GREATER, 33 | OP_LESS, 34 | OP_ADD_F, 35 | OP_ADD_I, 36 | OP_ADD_STRING, 37 | OP_ADD_LIST, 38 | OP_ADD_MAP, 39 | OP_SUBTRACT_I, 40 | OP_SUBTRACT_F, 41 | OP_MULTIPLY_I, 42 | OP_MULTIPLY_F, 43 | OP_DIVIDE_I, 44 | OP_DIVIDE_F, 45 | OP_MOD_I, 46 | OP_MOD_F, 47 | OP_BNOT, 48 | OP_BAND, 49 | OP_BOR, 50 | OP_XOR, 51 | OP_SHL, 52 | OP_SHR, 53 | 54 | OP_UNWRAP, 55 | 56 | OP_NOT, 57 | OP_NEGATE_I, 58 | OP_NEGATE_F, 59 | 60 | OP_SWAP, 61 | OP_JUMP, 62 | OP_JUMP_IF_FALSE, 63 | OP_JUMP_IF_NOT_NULL, 64 | OP_LOOP, 65 | OP_STRING_FOREACH, 66 | OP_LIST_FOREACH, 67 | OP_RANGE_FOREACH, 68 | OP_ENUM_FOREACH, 69 | OP_MAP_FOREACH, 70 | OP_FIBER_FOREACH, 71 | 72 | OP_CALL, 73 | OP_TAIL_CALL, 74 | OP_CALL_INSTANCE_PROPERTY, 75 | OP_TAIL_CALL_INSTANCE_PROPERTY, 76 | OP_INSTANCE_INVOKE, 77 | OP_INSTANCE_TAIL_INVOKE, 78 | OP_PROTOCOL_INVOKE, 79 | OP_PROTOCOL_TAIL_INVOKE, 80 | OP_STRING_INVOKE, 81 | OP_PATTERN_INVOKE, 82 | OP_FIBER_INVOKE, 83 | OP_LIST_INVOKE, 84 | OP_MAP_INVOKE, 85 | OP_RANGE_INVOKE, 86 | 87 | OP_CLOSURE, 88 | OP_CLOSE_UPVALUE, 89 | 90 | OP_FIBER, 91 | OP_RESUME, 92 | OP_RESOLVE, 93 | OP_YIELD, 94 | 95 | OP_TRY, 96 | OP_TRY_END, 97 | OP_THROW, 98 | 99 | OP_RETURN, 100 | 101 | OP_OBJECT, 102 | OP_INSTANCE, 103 | OP_FCONTAINER_INSTANCE, 104 | OP_PROPERTY, 105 | OP_OBJECT_DEFAULT, 106 | OP_GET_OBJECT_PROPERTY, 107 | OP_GET_INSTANCE_PROPERTY, 108 | OP_GET_INSTANCE_METHOD, 109 | OP_GET_PROTOCOL_METHOD, 110 | OP_GET_FCONTAINER_INSTANCE_PROPERTY, 111 | OP_GET_LIST_PROPERTY, 112 | OP_GET_MAP_PROPERTY, 113 | OP_GET_STRING_PROPERTY, 114 | OP_GET_PATTERN_PROPERTY, 115 | OP_GET_FIBER_PROPERTY, 116 | OP_GET_RANGE_PROPERTY, 117 | OP_SET_OBJECT_PROPERTY, 118 | OP_SET_INSTANCE_PROPERTY, 119 | OP_SET_FCONTAINER_INSTANCE_PROPERTY, 120 | 121 | OP_GET_ENUM_CASE, 122 | OP_GET_ENUM_CASE_VALUE, 123 | OP_GET_ENUM_CASE_FROM_VALUE, 124 | 125 | OP_LIST, 126 | OP_RANGE, 127 | OP_LIST_APPEND, 128 | 129 | OP_MAP, 130 | // FIXME: delete and only use OP_SET_MAP_SUBSCRIPT 131 | OP_SET_MAP, 132 | 133 | OP_EXPORT, 134 | OP_IMPORT, 135 | 136 | OP_TO_STRING, 137 | OP_TYPEOF, 138 | 139 | OP_HOTSPOT, 140 | OP_HOTSPOT_CALL, 141 | }; 142 | 143 | /// A chunk of code to execute 144 | const Self = @This(); 145 | 146 | pub const max_constants = std.math.maxInt(u24); 147 | 148 | const RegistryContext = struct { 149 | pub fn hash(_: RegistryContext, key: Self) u64 { 150 | return std.hash.Wyhash.hash( 151 | 0, 152 | std.mem.sliceAsBytes(key.code.items), 153 | ); 154 | } 155 | 156 | pub fn eql(_: RegistryContext, a: Self, b: Self) bool { 157 | return std.mem.eql(u32, a.code.items, b.code.items) and 158 | std.mem.eql(Value, a.constants.items, b.constants.items); 159 | } 160 | }; 161 | 162 | pub fn HashMap(V: type) type { 163 | return std.HashMapUnmanaged( 164 | Self, 165 | V, 166 | RegistryContext, 167 | std.hash_map.default_max_load_percentage, 168 | ); 169 | } 170 | 171 | allocator: std.mem.Allocator, 172 | /// AST 173 | ast: Ast.Slice, 174 | // TODO: merge `code` and `lines` in a multiarray 175 | /// List of opcodes to execute 176 | code: std.ArrayListUnmanaged(u32), 177 | /// List of locations 178 | lines: std.ArrayListUnmanaged(Ast.TokenIndex), 179 | /// List of constants defined in this chunk 180 | constants: std.ArrayListUnmanaged(Value), 181 | 182 | pub fn init(allocator: std.mem.Allocator, ast: Ast.Slice) Self { 183 | return Self{ 184 | .allocator = allocator, 185 | .ast = ast, 186 | .code = .{}, 187 | .constants = .{}, 188 | .lines = .{}, 189 | }; 190 | } 191 | 192 | pub fn deinit(self: *Self) void { 193 | self.code.deinit(self.allocator); 194 | self.constants.deinit(self.allocator); 195 | self.lines.deinit(self.allocator); 196 | } 197 | 198 | pub fn write(self: *Self, code: u32, where: Ast.TokenIndex) !void { 199 | try self.code.append(self.allocator, code); 200 | try self.lines.append(self.allocator, where); 201 | } 202 | 203 | pub fn addConstant(self: *Self, vm: ?*VM, value: Value) !u24 { 204 | if (vm) |uvm| uvm.push(value); 205 | try self.constants.append(self.allocator, value); 206 | if (vm) |uvm| _ = uvm.pop(); 207 | 208 | return @intCast(self.constants.items.len - 1); 209 | } 210 | -------------------------------------------------------------------------------- /src/behavior.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const runFile = @import("main.zig").runFile; 3 | const io = @import("io.zig"); 4 | const Parser = @import("Parser.zig"); 5 | 6 | // Because of https://github.com/ziglang/zig/issues/15091 test that write to stdout will hang 7 | // However I think its completely legitimate for a tested code to output to stdout and I 8 | // don't really get why zig test needs to use stdout anyway 9 | 10 | const Result = struct { 11 | total: usize, 12 | failed: usize, 13 | }; 14 | 15 | fn testBehaviors(allocator: std.mem.Allocator) !Result { 16 | var count: usize = 0; 17 | var fail_count: usize = 0; 18 | 19 | var test_dir = try std.fs.cwd().openDir( 20 | "tests", 21 | .{ 22 | .iterate = true, 23 | }, 24 | ); 25 | var it = test_dir.iterate(); 26 | 27 | while (try it.next()) |file| : (count += 1) { 28 | if (file.kind == .file and std.mem.endsWith(u8, file.name, ".buzz")) { 29 | const file_name = try allocator.alloc(u8, 6 + file.name.len); 30 | defer allocator.free(file_name); 31 | 32 | var had_error: bool = false; 33 | runFile( 34 | allocator, 35 | try std.fmt.bufPrint(file_name, "tests/{s}", .{file.name}), 36 | &[_][:0]u8{}, 37 | .Test, 38 | ) catch { 39 | io.print("\u{001b}[31m[{s} ✕]\u{001b}[0m\n", .{file.name}); 40 | had_error = true; 41 | fail_count += 1; 42 | }; 43 | 44 | if (!had_error) { 45 | io.print("\u{001b}[32m[{s} ✓]\u{001b}[0m\n", .{file.name}); 46 | } 47 | } 48 | } 49 | 50 | return .{ 51 | .total = count, 52 | .failed = fail_count, 53 | }; 54 | } 55 | 56 | fn testCompileErrors(allocator: std.mem.Allocator) !Result { 57 | var count: usize = 0; 58 | var fail_count: usize = 0; 59 | 60 | var test_dir = try std.fs.cwd().openDir( 61 | "tests/compile_errors", 62 | .{ 63 | .iterate = true, 64 | }, 65 | ); 66 | var it = test_dir.iterate(); 67 | 68 | while (try it.next()) |file| : (count += 1) { 69 | if (file.kind == .file and std.mem.endsWith(u8, file.name, ".buzz")) { 70 | const file_name: []u8 = try allocator.alloc(u8, 21 + file.name.len); 71 | defer allocator.free(file_name); 72 | _ = try std.fmt.bufPrint(file_name, "tests/compile_errors/{s}", .{file.name}); 73 | 74 | // First line of test file is expected error message 75 | const test_file = try std.fs.cwd().openFile(file_name, .{ .mode = .read_only }); 76 | const reader = test_file.reader(); 77 | var first_line = try reader.readUntilDelimiterAlloc(allocator, '\n', std.math.maxInt(usize)); 78 | first_line = @constCast(std.mem.trim(u8, first_line, "\r\n")); 79 | defer allocator.free(first_line); 80 | const arg0 = std.fmt.allocPrintZ( 81 | allocator, 82 | "{s}/bin/buzz", 83 | .{ 84 | try Parser.buzzPrefix(allocator), 85 | }, 86 | ) catch unreachable; 87 | defer allocator.free(arg0); 88 | 89 | const result = try std.process.Child.run( 90 | .{ 91 | .allocator = allocator, 92 | .argv = ([_][]const u8{ 93 | arg0, 94 | "-t", 95 | file_name, 96 | })[0..], 97 | }, 98 | ); 99 | 100 | if (!std.mem.containsAtLeast(u8, result.stderr, 1, first_line[2..])) { 101 | fail_count += 1; 102 | io.print( 103 | "Expected error `{s}` got `{s}`\n", 104 | .{ 105 | first_line[2..], 106 | result.stderr, 107 | }, 108 | ); 109 | 110 | io.print("\u{001b}[31m[{s}... ✕]\u{001b}[0m\n", .{file.name}); 111 | } else { 112 | io.print("\u{001b}[32m[{s}... ✓]\u{001b}[0m\n", .{file.name}); 113 | } 114 | } 115 | } 116 | 117 | return .{ 118 | .total = count, 119 | .failed = fail_count, 120 | }; 121 | } 122 | 123 | pub fn main() !u8 { 124 | var gpa = std.heap.GeneralPurposeAllocator(.{ 125 | .safety = true, 126 | }){}; 127 | const allocator = gpa.allocator(); 128 | var count: usize = 0; 129 | var fail_count: usize = 0; 130 | 131 | const behaviors_result = try testBehaviors(allocator); 132 | const comp_result = try testCompileErrors(allocator); 133 | 134 | count += comp_result.total + behaviors_result.total; 135 | fail_count += comp_result.failed + behaviors_result.failed; 136 | 137 | if (fail_count == 0) { 138 | io.print("\n\u{001b}[32m", .{}); 139 | } else { 140 | io.print("\n\u{001b}[31m", .{}); 141 | } 142 | 143 | io.print("Ran {}, Failed: {}\u{001b}[0m\n", .{ 144 | count, 145 | fail_count, 146 | }); 147 | 148 | try std.testing.expect(fail_count == 0); 149 | 150 | return if (fail_count > 0) 1 else 0; 151 | } 152 | -------------------------------------------------------------------------------- /src/builtin.zig: -------------------------------------------------------------------------------- 1 | pub const str = @import("builtin/str.zig"); 2 | pub const list = @import("builtin/list.zig"); 3 | pub const map = @import("builtin/map.zig"); 4 | pub const fiber = @import("builtin/fiber.zig"); 5 | pub const pattern = @import("builtin/pattern.zig"); 6 | pub const range = @import("builtin/range.zig"); 7 | -------------------------------------------------------------------------------- /src/builtin/fiber.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const o = @import("../obj.zig"); 3 | const VM = @import("../vm.zig").VM; 4 | const v = @import("../value.zig"); 5 | 6 | pub fn over(ctx: *o.NativeCtx) callconv(.c) c_int { 7 | const self = o.ObjFiber.cast(ctx.vm.peek(0).obj()).?; 8 | 9 | ctx.vm.push(v.Value.fromBoolean(self.fiber.status == .Over)); 10 | 11 | return 1; 12 | } 13 | 14 | pub fn cancel(ctx: *o.NativeCtx) callconv(.c) c_int { 15 | const self = o.ObjFiber.cast(ctx.vm.peek(0).obj()).?; 16 | 17 | // Main fiber can't be cancelled 18 | if (self.fiber.parent_fiber == null) { 19 | return 0; 20 | } 21 | 22 | self.fiber.status = .Over; 23 | 24 | return 0; 25 | } 26 | 27 | pub fn isMain(ctx: *o.NativeCtx) callconv(.c) c_int { 28 | const self = o.ObjFiber.cast(ctx.vm.peek(0).obj()).?; 29 | 30 | ctx.vm.push(v.Value.fromBoolean(self.fiber.parent_fiber == null)); 31 | 32 | return 1; 33 | } 34 | -------------------------------------------------------------------------------- /src/io.zig: -------------------------------------------------------------------------------- 1 | // Because of https://ziglang.org/download/0.12.0/release-notes.html#Bring-Your-Own-OS-API-Layer-Regressed 2 | // we have to add this abstraction layer to avoid using io.getStdIn/Err/Out 3 | 4 | const std = @import("std"); 5 | const builtin = @import("builtin"); 6 | const is_wasm = builtin.cpu.arch.isWasm(); 7 | const wasm = @import("wasm.zig"); 8 | 9 | fn stdErrWrite(_: void, bytes: []const u8) std.posix.WriteError!usize { 10 | return std.io.getStdErr().write(bytes); 11 | } 12 | 13 | fn stdOutWrite(_: void, bytes: []const u8) std.posix.WriteError!usize { 14 | return std.io.getStdOut().write(bytes); 15 | } 16 | 17 | fn stdInRead(_: void, buffer: []u8) std.posix.ReadError!usize { 18 | return std.io.getStdIn().read(buffer); 19 | } 20 | 21 | const StdErrWriter = std.io.Writer( 22 | void, 23 | std.posix.WriteError, 24 | if (is_wasm) 25 | wasm.stdErrWrite 26 | else 27 | stdErrWrite, 28 | ); 29 | 30 | pub const stdErrWriter = StdErrWriter{ .context = {} }; 31 | 32 | const StdOutWriter = std.io.Writer( 33 | void, 34 | std.posix.WriteError, 35 | if (is_wasm) 36 | wasm.stdOutWrite 37 | else 38 | stdOutWrite, 39 | ); 40 | 41 | pub const stdOutWriter = StdOutWriter{ .context = {} }; 42 | 43 | const StdInReader = std.io.Reader( 44 | void, 45 | std.posix.ReadError, 46 | if (is_wasm) 47 | wasm.stdInRead 48 | else 49 | stdInRead, 50 | ); 51 | 52 | pub const stdInReader = StdInReader{ .context = {} }; 53 | 54 | var stderr_mutex = std.Thread.Mutex{}; 55 | 56 | pub fn print(comptime fmt: []const u8, args: anytype) void { 57 | if (is_wasm) { 58 | stderr_mutex.lock(); 59 | defer stderr_mutex.unlock(); 60 | 61 | stdErrWriter.print(fmt, args) catch return; 62 | } else { 63 | std.debug.print(fmt, args); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/jmp.zig: -------------------------------------------------------------------------------- 1 | pub const jmp_buf = [48]c_int; 2 | 3 | pub extern fn _setjmp([*c]c_int) c_int; 4 | pub extern fn setjmp([*c]c_int) c_int; 5 | pub extern fn longjmp([*c]c_int, c_int) noreturn; 6 | pub extern fn _longjmp([*c]c_int, c_int) noreturn; 7 | -------------------------------------------------------------------------------- /src/lib/buzz_crypto.zig: -------------------------------------------------------------------------------- 1 | const api = @import("buzz_api.zig"); 2 | const std = @import("std"); 3 | 4 | const builtin = @import("builtin"); 5 | const is_wasm = builtin.cpu.arch.isWasm(); 6 | 7 | pub const os = if (is_wasm) 8 | @import("wasm.zig") 9 | else 10 | std.os; 11 | 12 | fn bin2hex(allocator: std.mem.Allocator, input: []const u8) std.ArrayList(u8) { 13 | var result = std.ArrayList(u8).init(allocator); 14 | var writer = result.writer(); 15 | 16 | for (input) |byte| { 17 | writer.print("{x:0>2}", .{byte}) catch @panic("Could not convert string to hex"); 18 | } 19 | 20 | return result; 21 | } 22 | 23 | pub export fn hash(ctx: *api.NativeCtx) callconv(.c) c_int { 24 | const algo_index = ctx.vm.bz_peek(1).bz_getEnumInstanceValue().integer(); 25 | var data_len: usize = 0; 26 | const data = ctx.vm.bz_peek(0).bz_valueToString(&data_len) orelse @panic("Could not hash data"); 27 | 28 | // Since alog_index is not static, we're forced to repeat ourselves... 29 | var result_hash: []u8 = undefined; 30 | switch (algo_index) { 31 | 0 => { 32 | var h: [std.crypto.hash.Md5.digest_length]u8 = undefined; 33 | std.crypto.hash.Md5.hash(data[0..data_len], &h, .{}); 34 | result_hash = h[0..]; 35 | }, 36 | 1 => { 37 | var h: [std.crypto.hash.Sha1.digest_length]u8 = undefined; 38 | std.crypto.hash.Sha1.hash(data[0..data_len], &h, .{}); 39 | result_hash = h[0..]; 40 | }, 41 | 2 => { 42 | var h: [std.crypto.hash.sha2.Sha224.digest_length]u8 = undefined; 43 | std.crypto.hash.sha2.Sha224.hash(data[0..data_len], &h, .{}); 44 | result_hash = h[0..]; 45 | }, 46 | 3 => { 47 | var h: [std.crypto.hash.sha2.Sha256.digest_length]u8 = undefined; 48 | std.crypto.hash.sha2.Sha256.hash(data[0..data_len], &h, .{}); 49 | result_hash = h[0..]; 50 | }, 51 | 4 => { 52 | var h: [std.crypto.hash.sha2.Sha384.digest_length]u8 = undefined; 53 | std.crypto.hash.sha2.Sha384.hash(data[0..data_len], &h, .{}); 54 | result_hash = h[0..]; 55 | }, 56 | 5 => { 57 | var h: [std.crypto.hash.sha2.Sha512.digest_length]u8 = undefined; 58 | std.crypto.hash.sha2.Sha512.hash(data[0..data_len], &h, .{}); 59 | result_hash = h[0..]; 60 | }, 61 | 6 => { 62 | var h: [std.crypto.hash.sha2.Sha512_224.digest_length]u8 = undefined; 63 | std.crypto.hash.sha2.Sha512_224.hash(data[0..data_len], &h, .{}); 64 | result_hash = h[0..]; 65 | }, 66 | 7 => { 67 | var h: [std.crypto.hash.sha2.Sha512_256.digest_length]u8 = undefined; 68 | std.crypto.hash.sha2.Sha512_256.hash(data[0..data_len], &h, .{}); 69 | result_hash = h[0..]; 70 | }, 71 | 8 => { 72 | var h: [std.crypto.hash.sha2.Sha512T256.digest_length]u8 = undefined; 73 | std.crypto.hash.sha2.Sha512T256.hash(data[0..data_len], &h, .{}); 74 | result_hash = h[0..]; 75 | }, 76 | 9 => { 77 | var h: [std.crypto.hash.sha3.Sha3_224.digest_length]u8 = undefined; 78 | std.crypto.hash.sha3.Sha3_224.hash(data[0..data_len], &h, .{}); 79 | result_hash = h[0..]; 80 | }, 81 | 10 => { 82 | var h: [std.crypto.hash.sha3.Sha3_256.digest_length]u8 = undefined; 83 | std.crypto.hash.sha3.Sha3_256.hash(data[0..data_len], &h, .{}); 84 | result_hash = h[0..]; 85 | }, 86 | 11 => { 87 | var h: [std.crypto.hash.sha3.Sha3_384.digest_length]u8 = undefined; 88 | std.crypto.hash.sha3.Sha3_384.hash(data[0..data_len], &h, .{}); 89 | result_hash = h[0..]; 90 | }, 91 | 12 => { 92 | var h: [std.crypto.hash.sha3.Sha3_512.digest_length]u8 = undefined; 93 | std.crypto.hash.sha3.Sha3_512.hash(data[0..data_len], &h, .{}); 94 | result_hash = h[0..]; 95 | }, 96 | 97 | else => @panic("Unknown hash algorithm"), 98 | } 99 | 100 | ctx.vm.bz_push( 101 | api.VM.bz_stringToValue( 102 | ctx.vm, 103 | result_hash.ptr, 104 | result_hash.len, 105 | ), 106 | ); 107 | 108 | return 1; 109 | } 110 | -------------------------------------------------------------------------------- /src/lib/buzz_debug.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const api = @import("buzz_api.zig"); 3 | const io = @import("io.zig"); 4 | 5 | const builtin = @import("builtin"); 6 | const is_wasm = builtin.cpu.arch.isWasm(); 7 | 8 | pub const os = if (is_wasm) 9 | @import("wasm.zig") 10 | else 11 | std.os; 12 | 13 | pub export fn dump(ctx: *api.NativeCtx) callconv(.c) c_int { 14 | ctx.vm.bz_peek(0).bz_valueDump(ctx.vm); 15 | 16 | io.print("\n", .{}); 17 | 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /src/lib/buzz_ffi.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const api = @import("buzz_api.zig"); 3 | 4 | pub export fn alignOf(ctx: *api.NativeCtx) callconv(.c) c_int { 5 | var len: usize = 0; 6 | const zig_type_str = ctx.vm.bz_peek(0).bz_valueToString(&len).?; 7 | 8 | var expected_type: api.Value = undefined; 9 | const zig_type = ctx.vm.bz_zigType(zig_type_str, len, &expected_type); 10 | 11 | if (zig_type) |ztype| { 12 | ctx.vm.bz_push(api.Value.fromInteger(@intCast(ztype.bz_zigTypeAlignment()))); 13 | } else { 14 | var msg = std.ArrayList(u8).init(api.VM.allocator); 15 | defer msg.deinit(); 16 | 17 | msg.writer().print( 18 | "Could not parse zig type `{s}`", 19 | .{ 20 | zig_type_str[0..len], 21 | }, 22 | ) catch { 23 | ctx.vm.bz_panic("Out of memory", "Out of memory".len); 24 | unreachable; 25 | }; 26 | 27 | ctx.vm.pushError("ffi.FFIZigTypeParseError", msg.items); 28 | 29 | return -1; 30 | } 31 | 32 | return 1; 33 | } 34 | 35 | pub export fn sizeOf(ctx: *api.NativeCtx) callconv(.c) c_int { 36 | var len: usize = 0; 37 | const zig_type_str = ctx.vm.bz_peek(0).bz_valueToString(&len).?; 38 | 39 | var expected_type: api.Value = undefined; 40 | const zig_type = ctx.vm.bz_zigType(zig_type_str, len, &expected_type); 41 | 42 | if (zig_type) |ztype| { 43 | ctx.vm.bz_push(api.Value.fromInteger(@intCast(ztype.bz_zigTypeSize()))); 44 | } else { 45 | var msg = std.ArrayList(u8).init(api.VM.allocator); 46 | defer msg.deinit(); 47 | 48 | msg.writer().print( 49 | "Could not parse zig type `{s}`", 50 | .{ 51 | zig_type_str[0..len], 52 | }, 53 | ) catch { 54 | ctx.vm.bz_panic("Out of memory", "Out of memory".len); 55 | unreachable; 56 | }; 57 | 58 | ctx.vm.pushError("ffi.FFIZigTypeParseError", msg.items); 59 | 60 | return -1; 61 | } 62 | 63 | return 1; 64 | } 65 | 66 | // FIXME: raise error if typedef is not .ForeignContainer 67 | pub export fn sizeOfStruct(ctx: *api.NativeCtx) callconv(.c) c_int { 68 | const type_def = ctx.vm.bz_peek(0); 69 | 70 | ctx.vm.bz_push( 71 | api.Value.fromInteger(@intCast(type_def.bz_containerTypeSize())), 72 | ); 73 | 74 | return 1; 75 | } 76 | 77 | pub export fn alignOfStruct(ctx: *api.NativeCtx) callconv(.c) c_int { 78 | const type_def = ctx.vm.bz_peek(0); 79 | 80 | ctx.vm.bz_push( 81 | api.Value.fromInteger(@intCast(type_def.bz_containerTypeAlign())), 82 | ); 83 | 84 | return 1; 85 | } 86 | 87 | pub export fn rawData(ctx: *api.NativeCtx) callconv(.c) c_int { 88 | const data = ctx.vm.bz_peek(0); 89 | 90 | if (!data.bz_valueIsForeignContainer()) { 91 | ctx.vm.pushError("ffi.ValueNotForeignContainer", null); 92 | 93 | return -1; 94 | } 95 | 96 | var len: usize = 0; 97 | const data_ptr = data.bz_foreignContainerSlice(&len); 98 | 99 | ctx.vm.bz_push( 100 | ctx.vm.bz_stringToValue(data_ptr, len), 101 | ); 102 | 103 | return 1; 104 | } 105 | -------------------------------------------------------------------------------- /src/lib/buzz_gc.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const api = @import("buzz_api.zig"); 3 | 4 | const builtin = @import("builtin"); 5 | const is_wasm = builtin.cpu.arch.isWasm(); 6 | 7 | pub const os = if (is_wasm) 8 | @import("wasm.zig") 9 | else 10 | std.os; 11 | 12 | pub export fn allocated(ctx: *api.NativeCtx) callconv(.c) c_int { 13 | ctx.vm.bz_push(api.Value.fromInteger(@intCast(ctx.vm.bz_allocated()))); 14 | 15 | return 1; 16 | } 17 | 18 | pub export fn collect(ctx: *api.NativeCtx) callconv(.c) c_int { 19 | ctx.vm.bz_collect(); 20 | 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /src/lib/buzz_math.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const api = @import("buzz_api.zig"); 3 | 4 | const builtin = @import("builtin"); 5 | const is_wasm = builtin.cpu.arch.isWasm(); 6 | 7 | pub const os = if (is_wasm) 8 | @import("wasm.zig") 9 | else 10 | std.os; 11 | 12 | pub export fn abs(ctx: *api.NativeCtx) callconv(.c) c_int { 13 | const n_f: api.Double = ctx.vm.bz_peek(0).double(); 14 | 15 | ctx.vm.bz_push(api.Value.fromDouble(if (n_f < 0) n_f * -1 else n_f)); 16 | 17 | return 1; 18 | } 19 | 20 | pub export fn acos(ctx: *api.NativeCtx) callconv(.c) c_int { 21 | const n_f: api.Double = ctx.vm.bz_peek(0).double(); 22 | 23 | ctx.vm.bz_push(api.Value.fromDouble(std.math.acos(n_f))); 24 | 25 | return 1; 26 | } 27 | 28 | pub export fn asin(ctx: *api.NativeCtx) callconv(.c) c_int { 29 | const n_f: api.Double = ctx.vm.bz_peek(0).double(); 30 | 31 | ctx.vm.bz_push(api.Value.fromDouble(std.math.asin(n_f))); 32 | 33 | return 1; 34 | } 35 | 36 | pub export fn atan(ctx: *api.NativeCtx) callconv(.c) c_int { 37 | const n_f: api.Double = ctx.vm.bz_peek(0).double(); 38 | 39 | ctx.vm.bz_push(api.Value.fromDouble(std.math.atan(n_f))); 40 | 41 | return 1; 42 | } 43 | 44 | pub export fn bzceil(ctx: *api.NativeCtx) callconv(.c) c_int { 45 | const n_f: api.Double = ctx.vm.bz_peek(0).double(); 46 | 47 | ctx.vm.bz_push(api.Value.fromInteger(@intFromFloat(std.math.ceil(n_f)))); 48 | 49 | return 1; 50 | } 51 | 52 | pub export fn bzcos(ctx: *api.NativeCtx) callconv(.c) c_int { 53 | const n_f: api.Double = ctx.vm.bz_peek(0).double(); 54 | 55 | ctx.vm.bz_push(api.Value.fromDouble(std.math.cos(n_f))); 56 | 57 | return 1; 58 | } 59 | 60 | pub export fn bzexp(ctx: *api.NativeCtx) callconv(.c) c_int { 61 | const n_f: api.Double = ctx.vm.bz_peek(0).double(); 62 | 63 | ctx.vm.bz_push(api.Value.fromDouble(std.math.exp(n_f))); 64 | 65 | return 1; 66 | } 67 | 68 | pub export fn bzfloor(ctx: *api.NativeCtx) callconv(.c) c_int { 69 | const n_f: api.Double = ctx.vm.bz_peek(0).double(); 70 | 71 | ctx.vm.bz_push(api.Value.fromInteger(@intFromFloat(std.math.floor(n_f)))); 72 | 73 | return 1; 74 | } 75 | 76 | pub export fn bzlog(ctx: *api.NativeCtx) callconv(.c) c_int { 77 | const base_i: api.Double = ctx.vm.bz_peek(1).double(); 78 | const n_f: api.Double = ctx.vm.bz_peek(0).double(); 79 | 80 | ctx.vm.bz_push(api.Value.fromDouble(std.math.log(api.Double, base_i, n_f))); 81 | 82 | return 1; 83 | } 84 | 85 | pub export fn maxDouble(ctx: *api.NativeCtx) callconv(.c) c_int { 86 | const a_f = ctx.vm.bz_peek(0).double(); 87 | const b_f = ctx.vm.bz_peek(1).double(); 88 | 89 | ctx.vm.bz_push(api.Value.fromDouble(@max(a_f, b_f))); 90 | 91 | return 1; 92 | } 93 | 94 | pub export fn minDouble(ctx: *api.NativeCtx) callconv(.c) c_int { 95 | const a_f = ctx.vm.bz_peek(0).double(); 96 | const b_f = ctx.vm.bz_peek(1).double(); 97 | 98 | ctx.vm.bz_push(api.Value.fromDouble(@min(a_f, b_f))); 99 | 100 | return 1; 101 | } 102 | 103 | pub export fn maxInt(ctx: *api.NativeCtx) callconv(.c) c_int { 104 | const a_f = ctx.vm.bz_peek(0).integer(); 105 | const b_f = ctx.vm.bz_peek(1).integer(); 106 | 107 | ctx.vm.bz_push(api.Value.fromInteger(@max(a_f, b_f))); 108 | 109 | return 1; 110 | } 111 | 112 | pub export fn minInt(ctx: *api.NativeCtx) callconv(.c) c_int { 113 | const a_f = ctx.vm.bz_peek(0).integer(); 114 | const b_f = ctx.vm.bz_peek(1).integer(); 115 | 116 | ctx.vm.bz_push(api.Value.fromInteger(@min(a_f, b_f))); 117 | 118 | return 1; 119 | } 120 | 121 | pub export fn bzsin(ctx: *api.NativeCtx) callconv(.c) c_int { 122 | const n: api.Double = ctx.vm.bz_peek(0).double(); 123 | 124 | ctx.vm.bz_push(api.Value.fromDouble(std.math.sin(n))); 125 | 126 | return 1; 127 | } 128 | 129 | pub export fn bzsqrt(ctx: *api.NativeCtx) callconv(.c) c_int { 130 | const n_f: api.Double = ctx.vm.bz_peek(0).double(); 131 | 132 | ctx.vm.bz_push(api.Value.fromDouble(std.math.sqrt(n_f))); 133 | 134 | return 1; 135 | } 136 | 137 | pub export fn bztan(ctx: *api.NativeCtx) callconv(.c) c_int { 138 | const n: api.Double = ctx.vm.bz_peek(0).double(); 139 | 140 | ctx.vm.bz_push(api.Value.fromDouble(std.math.tan(n))); 141 | 142 | return 1; 143 | } 144 | 145 | pub export fn pow(ctx: *api.NativeCtx) callconv(.c) c_int { 146 | const n = ctx.vm.bz_peek(1); 147 | const p = ctx.vm.bz_peek(0); 148 | 149 | const n_i: ?api.Integer = if (n.isInteger()) n.integer() else null; 150 | const n_f: ?api.Double = if (n.isFloat()) n.double() else null; 151 | 152 | const p_i: ?api.Integer = if (p.isInteger()) p.integer() else null; 153 | const p_f: ?api.Double = if (p.isFloat()) p.double() else null; 154 | 155 | if (p_f) |pf| { 156 | ctx.vm.bz_push(api.Value.fromDouble( 157 | std.math.pow(api.Double, n_f orelse @as(api.Double, @floatFromInt(n_i.?)), pf), 158 | )); 159 | } else if (n_f) |nf| { 160 | ctx.vm.bz_push(api.Value.fromDouble( 161 | std.math.pow(api.Double, nf, p_f orelse @as(api.Double, @floatFromInt(p_i.?))), 162 | )); 163 | } else { 164 | ctx.vm.bz_push( 165 | api.Value.fromInteger( 166 | std.math.powi(api.Integer, n_i.?, p_i.?) catch |err| { 167 | switch (err) { 168 | error.Overflow => ctx.vm.pushError("errors.OverflowError", null), 169 | error.Underflow => ctx.vm.pushError("errors.UnderflowError", null), 170 | } 171 | 172 | return -1; 173 | }, 174 | ), 175 | ); 176 | } 177 | 178 | return 1; 179 | } 180 | -------------------------------------------------------------------------------- /src/lib/buzz_serialize.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const api = @import("buzz_api.zig"); 3 | 4 | const builtin = @import("builtin"); 5 | const is_wasm = builtin.cpu.arch.isWasm(); 6 | 7 | pub const os = if (is_wasm) 8 | @import("wasm.zig") 9 | else 10 | std.os; 11 | 12 | pub export fn serializeValue(ctx: *api.NativeCtx) callconv(.c) c_int { 13 | const to_serialize = ctx.vm.bz_peek(0); 14 | 15 | var error_value = api.Value.Void; 16 | const serialized = ctx.vm.bz_serialize(to_serialize, &error_value); 17 | 18 | if (error_value.val != api.Value.Void.val) { 19 | ctx.vm.bz_push(error_value); 20 | 21 | return -1; 22 | } 23 | 24 | ctx.vm.bz_push(serialized); 25 | 26 | return 1; 27 | } 28 | -------------------------------------------------------------------------------- /src/lib/crypto.buzz: -------------------------------------------------------------------------------- 1 | namespace crypto; 2 | 3 | /// Hash algorithms 4 | export enum HashAlgorithm { 5 | Md5, 6 | Sha1, 7 | Sha224, 8 | Sha256, 9 | Sha384, 10 | Sha512, 11 | Sha512_224, 12 | Sha512_256, 13 | Sha512T256, 14 | Sha3224, 15 | Sha3256, 16 | Sha3384, 17 | Sha3512, 18 | } 19 | 20 | /// Returns hash of data using given algorithm 21 | /// @param algo Hash algorithm to use 22 | /// @param data Data to hash 23 | /// @return Hash of data has hex string 24 | export extern fun hash(algo: HashAlgorithm, data: str) > str; 25 | -------------------------------------------------------------------------------- /src/lib/debug.buzz: -------------------------------------------------------------------------------- 1 | namespace debug; 2 | 3 | /// Dump any value to stdout 4 | export extern fun dump(value: any) > void; 5 | 6 | // Parse `source` and return the abstract syntax tree in JSON 7 | // @param source the buzz source 8 | // @param script name (used to fetch eventual extern functions) 9 | // @return AST as JSON 10 | // TODO: reactivate 11 | // export extern fun ast(source: str, scriptName: str) > str !> CompileError; 12 | -------------------------------------------------------------------------------- /src/lib/errors.buzz: -------------------------------------------------------------------------------- 1 | namespace errors; 2 | 3 | export enum FileSystemError { 4 | AccessDenied, 5 | AntivirusInterference, 6 | BadPathName, 7 | DeviceBusy, 8 | DiskQuota, 9 | FileBusy, 10 | FileLocksNotSupported, 11 | FileNotFound, 12 | FileSystem, 13 | InvalidBatchScriptArg, 14 | FileTooBig, 15 | InputOutput, 16 | InvalidHandle, 17 | InvalidUtf8, 18 | InvalidWtf8, 19 | IsDir, 20 | LinkQuotaExceeded, 21 | NameTooLong, 22 | NetworkNotFound, 23 | NoDevice, 24 | NonCanonical, 25 | NoSpaceLeft, 26 | NotDir, 27 | NotSupported, 28 | PathAlreadyExists, 29 | PermissionDenied, 30 | PipeBusy, 31 | ProcessFdQuotaExceeded, 32 | ReadOnlyFileSystem, 33 | RenameAcrossMountPoints, 34 | SharingViolation, 35 | SocketNotConnected, 36 | SymLinkLoop, 37 | SystemFdQuotaExceeded, 38 | SystemResources, 39 | UnrecognizedVolume, 40 | WouldBlock, 41 | } 42 | 43 | export enum ExecError { 44 | ChildExecFailed, 45 | CurrentWorkingDirectoryUnlinked, 46 | InvalidExe, 47 | InvalidFileDescriptor, 48 | InvalidName, 49 | InvalidUserId, 50 | PermissionDenied, 51 | ResourceLimitReached, 52 | TooBig, 53 | WaitAbandoned, 54 | WaitTimeOut, 55 | ProcessAlreadyExec, 56 | InvalidProcessGroupId, 57 | ProcessNotFound, 58 | } 59 | 60 | export enum SocketError { 61 | AddressFamilyNotSupported, 62 | AddressInUse, 63 | AddressNotAvailable, 64 | AddressNotResolved, 65 | AlreadyBound, 66 | AlreadyConnected, 67 | BlockedByFirewall, 68 | ConnectionAborted, 69 | ConnectionPending, 70 | ConnectionRefused, 71 | ConnectionResetByPeer, 72 | ConnectionTimedOut, 73 | Canceled, 74 | FileDescriptorNotASocket, 75 | HostLacksNetworkAddresses, 76 | Incomplete, 77 | InterfaceNotFound, 78 | InvalidCharacter, 79 | InvalidEnd, 80 | InvalidIPAddressFormat, 81 | InvalidIpv4Mapping, 82 | InvalidProtocolOption, 83 | MessageTooBig, 84 | NameServerFailure, 85 | NetworkSubsystemFailed, 86 | NetworkUnreachable, 87 | OperationNotSupported, 88 | PermissionDenied, 89 | ProcessFdQuotaExceeded, 90 | ProtocolFailure, 91 | ProtocolFamilyNotAvailable, 92 | ProtocolNotSupported, 93 | ServiceUnavailable, 94 | SocketNotBound, 95 | SocketNotListening, 96 | SocketTypeNotSupported, 97 | SystemFdQuotaExceeded, 98 | SystemResources, 99 | TemporaryNameServerFailure, 100 | TimeoutTooBig, 101 | UnknownHostName, 102 | WouldBlock, 103 | } 104 | 105 | export enum ReadWriteError { 106 | BrokenPipe, 107 | ConnectionResetByPeer, 108 | ConnectionTimedOut, 109 | LockViolation, 110 | NetNameDeleted, 111 | NotOpenForReading, 112 | NotOpenForWriting, 113 | OperationAborted, 114 | StreamTooLong, 115 | } 116 | 117 | export object CompileError { 118 | message: str = "CompileError", 119 | } 120 | export object InterpretError { 121 | message: str = "InterpretError", 122 | } 123 | export object InvalidArgumentError { 124 | message: str = "InvalidArgumentError", 125 | } 126 | export object NotYetImplementedError { 127 | message: str = "NotYetImplementedError", 128 | } 129 | export object OverflowError { 130 | message: str = "OverflowError", 131 | } 132 | export object UnderflowError { 133 | message: str = "UnderflowError", 134 | } 135 | export object UnexpectedError { 136 | message: str = "UnexpectedError", 137 | } 138 | -------------------------------------------------------------------------------- /src/lib/ffi.buzz: -------------------------------------------------------------------------------- 1 | namespace ffi; 2 | 3 | export fun cstr(string: str) => "{string}\0"; 4 | 5 | export object FFITypeMismatchError { 6 | message: str = "Provided buzz value type does not match expected FFI type", 7 | } 8 | 9 | export object FFIZigTypeParseError { 10 | message: str = "Could not parse zig type", 11 | } 12 | 13 | export object ValueNotForeignContainer { 14 | message: str = "Value is not a foreign container", 15 | } 16 | 17 | export extern fun alignOf(zigType: str) > int; 18 | 19 | export extern fun sizeOf(zigType: str) > int; 20 | 21 | export extern fun sizeOfStruct(structType: type) > int; 22 | 23 | export extern fun alignOfStruct(structType: type) > int; 24 | 25 | export extern fun rawData(data: any) > str; 26 | -------------------------------------------------------------------------------- /src/lib/fs.buzz: -------------------------------------------------------------------------------- 1 | namespace fs; 2 | 3 | import "os"; 4 | import "errors"; 5 | 6 | /// Returns current directory absolute path 7 | /// @return current directory 8 | export fun currentDirectory() > str !> errors\FileSystemError, errors\InvalidArgumentError { 9 | if (os\env("PWD") -> dir) { 10 | return dir; 11 | } else { 12 | throw errors\FileSystemError.BadPathName; 13 | } 14 | 15 | // TODO: should not be required since there's throw in the else branch 16 | return ""; 17 | } 18 | 19 | /// Creates directory path 20 | /// @param path directory to create 21 | export extern fun makeDirectory(path: str) > void !> errors\FileSystemError, errors\UnexpectedError; 22 | 23 | /// Deletes directory or file at path 24 | /// @param path direcotry/file to delete 25 | export extern fun delete(path: str) > void !> errors\FileSystemError, errors\UnexpectedError; 26 | 27 | /// Moves/renames file 28 | /// @param source file to move 29 | /// @param destination where to move it 30 | export extern fun move(source: str, destination: str) > void !> errors\FileSystemError, errors\UnexpectedError; 31 | 32 | /// List files under path 33 | /// @param path directory to list 34 | export extern fun list(path: str) > [str] !> errors\FileSystemError, errors\UnexpectedError; 35 | 36 | /// Returns true if path exists 37 | /// @param path directory/file to test 38 | /// @return wether file exists 39 | export extern fun exists(path: str) > bool !> errors\FileSystemError; 40 | -------------------------------------------------------------------------------- /src/lib/gc.buzz: -------------------------------------------------------------------------------- 1 | namespace gc; 2 | 3 | /// Error occured while collecting 4 | object CollectError {} 5 | 6 | /// Returns the number of allocated bytes 7 | /// @return allocated bytes 8 | export extern fun allocated() > int; 9 | 10 | /// Triggers a GC sweep 11 | export extern fun collect() > void !> CollectError; -------------------------------------------------------------------------------- /src/lib/io.buzz: -------------------------------------------------------------------------------- 1 | namespace io; 2 | 3 | import "errors"; 4 | 5 | /// @private 6 | extern fun FileOpen(filename: str, mode: int) > int !> errors\FileSystemError, errors\UnexpectedError; 7 | /// @private 8 | extern fun FileClose(fd: int) > void; 9 | /// @private 10 | extern fun FileReadAll(fd: int, maxSize: int?) > str !> errors\ReadWriteError, errors\FileSystemError, errors\UnexpectedError; 11 | /// @private 12 | extern fun FileReadLine(fd: int, maxSize: int?) > str? !> errors\ReadWriteError, errors\FileSystemError, errors\UnexpectedError; 13 | /// @private 14 | extern fun FileRead(fd: int, n: int) > str? !> errors\ReadWriteError, errors\FileSystemError, errors\InvalidArgumentError, errors\UnexpectedError; 15 | /// @private 16 | extern fun FileWrite(fd: int, bytes: str) > void !> errors\FileSystemError, errors\ReadWriteError, errors\UnexpectedError; 17 | /// @private 18 | extern fun getStdIn() > int; 19 | /// @private 20 | extern fun getStdOut() > int; 21 | /// @private 22 | extern fun getStdErr() > int; 23 | /// @private 24 | extern fun FileIsTTY(fd: int) > bool; 25 | /// @private 26 | extern fun FileGetPoller(fd: int) > ud !> errors\FileSystemError, errors\ReadWriteError, errors\SocketError, errors\ExecError, error\UnexpectedError; 27 | /// @private 28 | extern fun PollerPoll(poller: ud, timeout: int?) > str? !> errors\ReadWriteError; 29 | /// @private 30 | extern fun PollerDeinit(poller: ud) > void; 31 | 32 | /// File mode with which you can open a file 33 | export enum FileMode { 34 | read, 35 | write, 36 | update, 37 | } 38 | 39 | /// Object to manipulate an opened file 40 | export object File { 41 | /// File descriptor 42 | fd: int, 43 | 44 | /// Open file 45 | /// @param filename Path of file to open 46 | /// @param mode Mode with which to open it 47 | /// @return opened file 48 | static fun open(filename: str, mode: FileMode) > File !> errors\FileSystemError, errors\UnexpectedError { 49 | return File { 50 | fd = FileOpen(filename, mode: mode.value), 51 | }; 52 | } 53 | 54 | fun collect() > void { 55 | this.close(); 56 | } 57 | 58 | /// Close file 59 | fun close() > void { 60 | FileClose(this.fd); 61 | } 62 | 63 | /// Reads file until `EOF` 64 | /// @return Read data 65 | fun readAll(maxSize: int?) > str !> errors\ReadWriteError, errors\FileSystemError, errors\UnexpectedError { 66 | return FileReadAll(this.fd, maxSize); 67 | } 68 | 69 | /// Reads next line, returns null if nothing to read 70 | /// @return Read data 71 | fun readLine(maxSize: int?) > str? !> errors\ReadWriteError, errors\FileSystemError, errors\UnexpectedError { 72 | return FileReadLine(this.fd, maxSize); 73 | } 74 | 75 | /// Reads `n` bytes, returns null if nothing to read 76 | /// @param n how many bytes to read 77 | /// @return Read data 78 | fun read(n: int) > str? !> errors\ReadWriteError, errors\FileSystemError, errors\InvalidArgumentError, errors\UnexpectedError { 79 | return FileRead(this.fd, n: n); 80 | } 81 | 82 | /// Write bytes 83 | /// @param bytes string to write 84 | fun write(bytes: str) > void !> errors\FileSystemError, errors\ReadWriteError, errors\UnexpectedError { 85 | FileWrite(this.fd, bytes: bytes); 86 | } 87 | 88 | /// @return true if file is TTY 89 | fun isTTY() > bool { 90 | return FileIsTTY(this.fd); 91 | } 92 | 93 | /// @return FilePoller that can be used to poll incoming data on that file 94 | fun getPoller() > FilePoller !> errors\FileSystemError, errors\ReadWriteError, errors\SocketError, errors\ExecError, error\UnexpectedError { 95 | return FilePoller{ 96 | poller = FileGetPoller(this.fd), 97 | }; 98 | } 99 | } 100 | 101 | export object FilePoller { 102 | /// Underlying zig poller 103 | poller: ud, 104 | 105 | // Poll file, blocking for at most `timeout` before returning 106 | // @returns The string read if any, `null` otherwise 107 | fun poll(timeout: int?) > str? !> errors\ReadWriteError { 108 | return PollerPoll(this.poller, timeout); 109 | } 110 | 111 | fun collect() > void { 112 | this.deinit(); 113 | } 114 | 115 | fun deinit() > void { 116 | PollerDeinit(this.poller); 117 | } 118 | 119 | } 120 | 121 | /// stdin 122 | export final stdin = File{ fd = getStdIn() }; 123 | /// stdout 124 | export final stdout = File{ fd = getStdOut() }; 125 | /// stderr 126 | export final stderr = File{ fd = getStdErr() }; 127 | 128 | /// Run a buzz file 129 | /// @param filename path to buzz file 130 | export extern fun runFile(filename: str) > void !> errors\CompileError, errors\InterpretError, errors\FileSystemError, errors\ReadWriteError; 131 | -------------------------------------------------------------------------------- /src/lib/io.zig: -------------------------------------------------------------------------------- 1 | ../io.zig -------------------------------------------------------------------------------- /src/lib/jmp.zig: -------------------------------------------------------------------------------- 1 | pub const jmp_buf = [48]c_int; 2 | 3 | pub extern fn _setjmp([*c]c_int) c_int; 4 | pub extern fn setjmp([*c]c_int) c_int; 5 | pub extern fn longjmp([*c]c_int, c_int) noreturn; 6 | pub extern fn _longjmp([*c]c_int, c_int) noreturn; 7 | -------------------------------------------------------------------------------- /src/lib/math.buzz: -------------------------------------------------------------------------------- 1 | namespace math; 2 | 3 | import "errors"; 4 | 5 | /// @return absolute value of n 6 | extern fun abs(n: double) > double; 7 | 8 | /// @return acos of n 9 | extern fun acos(n: double) > double; 10 | 11 | /// @return asin of n 12 | extern fun asin(n: double) > double; 13 | 14 | /// @return atan of n 15 | extern fun atan(n: double) > double; 16 | 17 | /// @return ceiled n 18 | extern fun bzceil(n: double) > int; 19 | 20 | /// @return cos of n 21 | extern fun bzcos(n: double) > double; 22 | 23 | /// π 24 | final pi: double = 3.1415926535898; 25 | 26 | /// Convert radian to degree 27 | fun deg(n: double) > double { 28 | return n * 180.0 / pi; 29 | } 30 | 31 | /// @return exp of n 32 | extern fun bzexp(n: double) > double; 33 | 34 | /// @returned floored n 35 | extern fun bzfloor(n: double) > int; 36 | 37 | /// @return log(base) of n 38 | extern fun bzlog(base: double, n: double) > double; 39 | 40 | /// @return max of a and b 41 | extern fun maxDouble(a: double, b: double) > double; 42 | 43 | /// @return min of a and b 44 | extern fun minDouble(a: double, b: double) > double; 45 | 46 | /// @return max of a and b 47 | extern fun maxInt(a: int, b: int) > int; 48 | 49 | /// @return min of a and b 50 | extern fun minInt(a: int, b: int) > int; 51 | 52 | /// Convert degree to radian 53 | fun rad(n: double) > double { 54 | return n * pi / 180.0; 55 | } 56 | 57 | /// @return sin of n 58 | extern fun bzsin(n: double) > double; 59 | 60 | /// @return square root of n 61 | extern fun bzsqrt(n: double) > double; 62 | 63 | /// @return tan of n 64 | extern fun bztan(n: double) > double; 65 | 66 | /// @return `x`^`y` 67 | extern fun pow(x: double, y: double) > double !> errors\OverflowError, errors\UnderflowError; 68 | 69 | export abs; 70 | export acos; 71 | export asin; 72 | export atan; 73 | export bzsqrt as sqrt; 74 | export bzceil as ceil; 75 | export bzcos as cos; 76 | export deg; 77 | export bzexp as exp; 78 | export bzfloor as floor; 79 | export bzlog as log; 80 | export minDouble; 81 | export maxDouble; 82 | export minInt; 83 | export maxInt; 84 | export pi; 85 | export rad; 86 | export bzsin as sin; 87 | export bztan as tan; 88 | export pow; 89 | -------------------------------------------------------------------------------- /src/lib/mimalloc.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const mem = std.mem; 3 | const math = std.math; 4 | const debug = std.debug; 5 | 6 | const Allocator = mem.Allocator; 7 | 8 | extern fn mi_malloc_aligned(size: usize, alignment: usize) ?*anyopaque; 9 | extern fn mi_usable_size(p: ?*const anyopaque) usize; 10 | extern fn mi_expand(p: ?*anyopaque, newsize: usize) ?*anyopaque; 11 | extern fn mi_free(p: ?*anyopaque) void; 12 | 13 | const MimAllocator = struct { 14 | fn alloc( 15 | _: *anyopaque, 16 | len: usize, 17 | log2_align: mem.Alignment, 18 | _: usize, 19 | ) ?[*]u8 { 20 | return @ptrCast( 21 | mi_malloc_aligned( 22 | len, 23 | log2_align.toByteUnits(), 24 | ), 25 | ); 26 | } 27 | 28 | fn resize( 29 | _: *anyopaque, 30 | buf: []u8, 31 | _: mem.Alignment, 32 | new_len: usize, 33 | _: usize, 34 | ) bool { 35 | if (new_len > buf.len) { 36 | const available = mi_usable_size(buf.ptr); 37 | if (available > new_len) { 38 | if (mi_expand(buf.ptr, new_len)) |_| { 39 | return true; 40 | } 41 | } 42 | return false; 43 | } else { 44 | return true; 45 | } 46 | } 47 | 48 | fn free( 49 | _: *anyopaque, 50 | buf: []u8, 51 | _: mem.Alignment, 52 | _: usize, 53 | ) void { 54 | mi_free(buf.ptr); 55 | } 56 | 57 | fn remap( 58 | ctx: *anyopaque, 59 | buf: []u8, 60 | log2_align: mem.Alignment, 61 | new_len: usize, 62 | return_address: usize, 63 | ) ?[*]u8 { 64 | return if (resize(ctx, buf, log2_align, new_len, return_address)) 65 | buf.ptr 66 | else 67 | null; 68 | } 69 | }; 70 | 71 | pub const mim_allocator = Allocator{ 72 | .ptr = undefined, 73 | .vtable = &mim_allocator_vtable, 74 | }; 75 | const mim_allocator_vtable = Allocator.VTable{ 76 | .alloc = MimAllocator.alloc, 77 | .resize = MimAllocator.resize, 78 | .free = MimAllocator.free, 79 | .remap = MimAllocator.remap, 80 | }; 81 | -------------------------------------------------------------------------------- /src/lib/std.buzz: -------------------------------------------------------------------------------- 1 | namespace std; 2 | 3 | /// If condition is false print message and exit program 4 | /// @param condition assert condition 5 | /// @param message message printed if `condition` is false 6 | export extern fun assert(condition: bool, message: str? = null) > void; 7 | 8 | /// Prints value on stdout 9 | /// @param value value to print 10 | export extern fun print(value: str) > void; 11 | 12 | /// Parse integer, returns false if string does not represent a integer 13 | /// @param string string to parse 14 | /// @return integer parsed or null 15 | export extern fun parseInt(string: str) > int?; 16 | 17 | /// Parse double, returns false if string does not represent a double 18 | /// @param string string to parse 19 | /// @return double parsed or null 20 | export extern fun parseDouble(string: str) > double?; 21 | 22 | /// Cast integer to a double value 23 | /// @param n value to cast 24 | /// @return casted value 25 | export extern fun toInt(n: double) > int; 26 | 27 | /// Cast double to a integer value 28 | /// @param n value to cast 29 | /// @return casted value 30 | export extern fun toDouble(n: int) > double; 31 | 32 | // FIXME: once we have type composition this might be more elegant 33 | /// Cast double or integer to userdata 34 | /// @param n value to cast 35 | /// @return casted value or 0 if value provided is not a number 36 | export extern fun toUd(n: any) > ud; 37 | 38 | /// Parse ud, returns false if string does not represent a ud (u64) 39 | /// @param string string to parse 40 | /// @return ud parsed or null 41 | export extern fun parseUd(string: str) > ud?; 42 | 43 | /// Return ascii char for given byte 44 | export extern fun char(byte: int) > str; 45 | 46 | /// Return evenly distributed random number between `min` and `max` 47 | /// @param min Minimum value, if omitted `0` 48 | /// @param max Maximum value, if omitted `min + 1` 49 | /// @return Random value 50 | export extern fun random(min: int? = null, max: int? = null) > int; 51 | 52 | /// @return Current fiber 53 | export extern fun currentFiber() > fib; 54 | 55 | /// Print message and exit program 56 | extern fun buzzPanic(message: str) > void; 57 | 58 | /// Returns the command line arguments with which the script was launched 59 | export extern fun args() > [str]; 60 | 61 | export buzzPanic as panic; 62 | -------------------------------------------------------------------------------- /src/lib/wasm.zig: -------------------------------------------------------------------------------- 1 | ../wasm.zig -------------------------------------------------------------------------------- /src/linenoise.zig: -------------------------------------------------------------------------------- 1 | pub const linenoiseState = opaque { 2 | // Non blocking API. 3 | pub extern fn linenoiseEditStart(stdind_fd: c_int, stdout_fd: c_int, buf: [*]u8, buflen: usize, prompt: [*:0]const u8) callconv(.c) c_int; 4 | pub extern fn linenoiseEditFeed() callconv(.c) [*:0]u8; 5 | pub extern fn linenoiseEditStop() callconv(.c) void; 6 | pub extern fn linenoiseHide() callconv(.c) void; 7 | pub extern fn linenoiseShow() callconv(.c) void; 8 | }; 9 | 10 | pub const linenoiseCompletions = extern struct { 11 | len: usize, 12 | cvec: *[*:0]u8, 13 | }; 14 | 15 | // Blocking API. 16 | pub extern fn linenoise(prompt: [*:0]const u8) callconv(.c) ?[*:0]const u8; 17 | 18 | // Completion API. 19 | pub const linenoiseCompletionCallback = fn ([*:0]const u8, *linenoiseCompletions) void; 20 | pub const linenoiseHintsCallback = fn ([*:0]const u8, *c_int, *c_int) [*:0]const u8; 21 | pub const linenoiseFreeHintsCallback = fn (*anyopaque) void; 22 | pub extern fn linenoiseSetCompletionCallback(callback: *linenoiseCompletionCallback) callconv(.c) void; 23 | pub extern fn linenoiseSetHintsCallback(callback: *linenoiseHintsCallback) callconv(.c) void; 24 | pub extern fn linenoiseSetFreeHintsCallback(callback: *linenoiseFreeHintsCallback) callconv(.c) void; 25 | pub extern fn linenoiseAddCompletion(completions: *linenoiseCompletions, completion: [*:0]const u8) callconv(.c) void; 26 | 27 | // History API. 28 | pub extern fn linenoiseHistoryAdd(line: [*:0]const u8) callconv(.c) c_int; 29 | pub extern fn linenoiseHistorySetMaxLen(len: c_int) callconv(.c) c_int; 30 | pub extern fn linenoiseHistorySave(filename: [*:0]const u8) callconv(.c) c_int; 31 | pub extern fn linenoiseHistoryLoad(filename: [*:0]const u8) callconv(.c) c_int; 32 | 33 | // Other utilities. 34 | pub extern fn linenoiseClearScreen() callconv(.c) void; 35 | pub extern fn linenoiseSetMultiLine(ml: c_int) callconv(.c) void; 36 | pub extern fn linenoisePrintKeyCodes() callconv(.c) void; 37 | pub extern fn linenoiseMaskModeEnable() callconv(.c) void; 38 | pub extern fn linenoiseMaskModeDisable() callconv(.c) void; 39 | -------------------------------------------------------------------------------- /src/mimalloc.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | const mem = std.mem; 3 | const math = std.math; 4 | const debug = std.debug; 5 | 6 | const Allocator = mem.Allocator; 7 | 8 | extern fn mi_malloc_aligned(size: usize, alignment: usize) ?*anyopaque; 9 | extern fn mi_usable_size(p: ?*const anyopaque) usize; 10 | extern fn mi_expand(p: ?*anyopaque, newsize: usize) ?*anyopaque; 11 | extern fn mi_free(p: ?*anyopaque) void; 12 | 13 | const MimAllocator = struct { 14 | fn alloc( 15 | _: *anyopaque, 16 | len: usize, 17 | log2_align: mem.Alignment, 18 | _: usize, 19 | ) ?[*]u8 { 20 | return @ptrCast( 21 | mi_malloc_aligned( 22 | len, 23 | log2_align.toByteUnits(), 24 | ), 25 | ); 26 | } 27 | 28 | fn resize( 29 | _: *anyopaque, 30 | buf: []u8, 31 | _: mem.Alignment, 32 | new_len: usize, 33 | _: usize, 34 | ) bool { 35 | if (new_len > buf.len) { 36 | const available = mi_usable_size(buf.ptr); 37 | if (available > new_len) { 38 | if (mi_expand(buf.ptr, new_len)) |_| { 39 | return true; 40 | } 41 | } 42 | return false; 43 | } else { 44 | return true; 45 | } 46 | } 47 | 48 | fn free( 49 | _: *anyopaque, 50 | buf: []u8, 51 | _: mem.Alignment, 52 | _: usize, 53 | ) void { 54 | mi_free(buf.ptr); 55 | } 56 | 57 | fn remap( 58 | ctx: *anyopaque, 59 | buf: []u8, 60 | log2_align: mem.Alignment, 61 | new_len: usize, 62 | return_address: usize, 63 | ) ?[*]u8 { 64 | return if (resize(ctx, buf, log2_align, new_len, return_address)) 65 | buf.ptr 66 | else 67 | null; 68 | } 69 | }; 70 | 71 | pub const mim_allocator = Allocator{ 72 | .ptr = undefined, 73 | .vtable = &mim_allocator_vtable, 74 | }; 75 | const mim_allocator_vtable = Allocator.VTable{ 76 | .alloc = MimAllocator.alloc, 77 | .resize = MimAllocator.resize, 78 | .free = MimAllocator.free, 79 | .remap = MimAllocator.remap, 80 | }; 81 | -------------------------------------------------------------------------------- /src/repl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Buzz WASM REPL 6 | 7 | 8 | 9 | 15 | 16 | 17 |

Buzz REPL

18 | 19 | 20 | 21 | 22 |

23 |   
24 | 
25 | 


--------------------------------------------------------------------------------
/src/tests/fmt.zig:
--------------------------------------------------------------------------------
  1 | const std = @import("std");
  2 | const assert = std.debug.assert;
  3 | const Ast = @import("../Ast.zig");
  4 | const Parser = @import("../Parser.zig");
  5 | const mem = @import("../memory.zig");
  6 | const Renderer = @import("../renderer.zig").Renderer;
  7 | 
  8 | fn testFmt(
  9 |     name: []const u8,
 10 |     src: []const u8,
 11 |     expected: []const u8,
 12 | ) !void {
 13 |     var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
 14 |     defer arena.deinit();
 15 |     const allocator = arena.allocator();
 16 | 
 17 |     var gc = try mem.GarbageCollector.init(allocator);
 18 |     gc.type_registry = try mem.TypeRegistry.init(&gc);
 19 |     var imports = std.StringHashMapUnmanaged(Parser.ScriptImport){};
 20 |     var parser = Parser.init(
 21 |         &gc,
 22 |         &imports,
 23 |         false,
 24 |         .Fmt,
 25 |     );
 26 | 
 27 |     var result = std.ArrayList(u8).init(allocator);
 28 | 
 29 |     if (parser.parse(src, name, name) catch null) |ast| {
 30 |         try Renderer(std.ArrayList(u8).Writer).render(
 31 |             allocator,
 32 |             result.writer(),
 33 |             ast,
 34 |         );
 35 | 
 36 |         try std.testing.expectEqualStrings(
 37 |             expected,
 38 |             result.items,
 39 |         );
 40 |     } else {
 41 |         try std.testing.expect(false);
 42 |     }
 43 | }
 44 | 
 45 | test "fmt" {
 46 |     const ignore = std.StaticStringMap(void).initComptime(
 47 |         .{
 48 |             // stringEscape escapes utf-8 graphemes (emojis)
 49 |             .{ "tests/061-utf8.buzz", {} },
 50 |             // statements that enforce newline before them don't take comment into account
 51 |             .{ "tests/042-anonymous-objects.buzz", {} },
 52 |             // bad identation of inline if-else else branch
 53 |             .{ "tests/052-inline-if.buzz", {} },
 54 |         },
 55 |     );
 56 | 
 57 |     var test_dir = try std.fs.cwd().openDir(
 58 |         "tests",
 59 |         .{
 60 |             .iterate = true,
 61 |         },
 62 |     );
 63 |     var it = test_dir.iterate();
 64 | 
 65 |     while (try it.next()) |entry| {
 66 |         if (entry.kind == .file and std.mem.endsWith(u8, entry.name, ".buzz")) {
 67 |             var file_name = try std.testing.allocator.alloc(u8, 6 + entry.name.len);
 68 |             defer std.testing.allocator.free(file_name);
 69 |             file_name = try std.fmt.bufPrint(
 70 |                 file_name,
 71 |                 "tests/{s}",
 72 |                 .{entry.name},
 73 |             );
 74 | 
 75 |             if (ignore.get(file_name) != null) {
 76 |                 continue;
 77 |             }
 78 | 
 79 |             std.debug.print("\n{s}\n", .{file_name});
 80 | 
 81 |             var file = (if (std.fs.path.isAbsolute(
 82 |                 file_name,
 83 |             ))
 84 |                 std.fs.openFileAbsolute(file_name, .{})
 85 |             else
 86 |                 std.fs.cwd().openFile(file_name, .{})) catch {
 87 |                 std.debug.print("File not found", .{});
 88 |                 return;
 89 |             };
 90 |             defer file.close();
 91 | 
 92 |             const source = try std.testing.allocator.alloc(u8, (try file.stat()).size);
 93 |             defer std.testing.allocator.free(source);
 94 | 
 95 |             _ = try file.readAll(source);
 96 | 
 97 |             try testFmt(
 98 |                 file_name,
 99 |                 source,
100 |                 source,
101 |             );
102 |         }
103 |     }
104 | }
105 | 


--------------------------------------------------------------------------------
/src/wasm.ts:
--------------------------------------------------------------------------------
 1 | const decoder = new TextDecoder()
 2 | const encoder = new TextEncoder()
 3 | 
 4 | const stdin = document.querySelector('#stdin') as HTMLInputElement
 5 | const stdout = document.querySelector('#stdout') as HTMLPreElement
 6 | 
 7 | // Unit is pages: 64kb
 8 | var memory = new WebAssembly.Memory({
 9 |   initial: 100,
10 |   maximum: 1000
11 | })
12 | 
13 | export type WasmImports = Readonly<{
14 |   memory: WebAssembly.Memory
15 |   initRepl(): number
16 |   runLine(ctx: number): void
17 | }>
18 | 
19 | function writeToStderr (stringPtr: number, stringLength: number): void {
20 |   let string = decoder.decode(
21 |     new Uint8Array(memory.buffer, stringPtr, stringLength),
22 |     {
23 |       stream: true
24 |     }
25 |   )
26 | 
27 |   stdout.textContent += string
28 | }
29 | 
30 | function readFromStdin (bufferPtr: number, bufferLength: number): number {
31 |   let value = stdin.value
32 |   let buffer = new Uint8Array(memory.buffer, bufferPtr, bufferLength)
33 | 
34 |   // Write input value into provided memory (truncate if too much)
35 |   buffer.set(encoder.encode(value).slice(0, bufferLength))
36 | 
37 |   stdin.value = ''
38 | 
39 |   return Math.min(bufferLength, value.length)
40 | }
41 | 
42 | // In our 'build.zig' file we have configured esbuild to replace all uses of this constant with the filename of the
43 | // compiled WebAssembly artifact.
44 | declare const __WASM_ARTIFACT_FILENAME: string
45 | 
46 | let wasmImports: WasmImports = (
47 |   await WebAssembly.instantiateStreaming(fetch(__WASM_ARTIFACT_FILENAME), {
48 |     env: {
49 |       memory: memory,
50 |       writeToStderr: writeToStderr,
51 |       readFromStdin: readFromStdin
52 |     }
53 |   } as const)
54 | ).instance.exports as WasmImports
55 | 
56 | let ctx = wasmImports.initRepl()
57 | 
58 | stdin.addEventListener('keydown', e => {
59 |   if (e.key == 'Enter' && stdin.value.length > 0) {
60 |     wasmImports.runLine(ctx)
61 |   }
62 | })
63 | 


--------------------------------------------------------------------------------
/src/wasm.zig:
--------------------------------------------------------------------------------
 1 | const std = @import("std");
 2 | 
 3 | extern fn writeToStderr(string_ptr: [*]const u8, string_length: usize) callconv(.c) void;
 4 | extern fn writeToStdout(string_ptr: [*]const u8, string_length: usize) callconv(.c) void;
 5 | extern fn readFromStdin(buffer_ptr: [*]const u8, buffer_length: usize) callconv(.c) isize;
 6 | 
 7 | pub fn stdErrWrite(_: void, bytes: []const u8) std.posix.WriteError!usize {
 8 |     writeToStderr(bytes.ptr, bytes.len);
 9 | 
10 |     return bytes.len;
11 | }
12 | 
13 | pub fn stdOutWrite(_: void, bytes: []const u8) std.posix.WriteError!usize {
14 |     writeToStderr(bytes.ptr, bytes.len);
15 | 
16 |     return bytes.len;
17 | }
18 | 
19 | pub fn stdInRead(_: void, buffer: []u8) std.posix.ReadError!usize {
20 |     if (buffer.len == 0) {
21 |         return 0;
22 |     }
23 | 
24 |     return @intCast(
25 |         readFromStdin(
26 |             buffer.ptr,
27 |             buffer.len,
28 |         ),
29 |     );
30 | }
31 | 
32 | pub const io = struct {
33 |     pub fn getStdInHandle() std.posix.fd_t {
34 |         return std.os.emscripten.STDIN_FILENO;
35 |     }
36 | 
37 |     pub fn getStdErrHandle() std.posix.fd_t {
38 |         return std.os.emscripten.STDERR_FILENO;
39 |     }
40 | 
41 |     pub fn getStdOutHandle() std.posix.fd_t {
42 |         return std.os.emscripten.STDOUT_FILENO;
43 |     }
44 | };
45 | 
46 | pub const system = struct {
47 |     var errno: E = undefined;
48 | 
49 |     pub const E = std.os.emscripten.E;
50 | 
51 |     pub fn getErrno(rc: anytype) E {
52 |         return if (rc == -1) errno else .SUCCESS;
53 |     }
54 | 
55 |     pub const fd_t = std.os.emscripten.fd_t;
56 | 
57 |     pub const STDERR_FILENO = std.os.emscripten.STDERR_FILENO;
58 |     pub const STDOUT_FILENO = std.os.emscripten.STDOUT_FILENO;
59 |     pub const STDIN_FILENO = std.os.emscripten.STDIN_FILENO;
60 | 
61 |     pub fn write(fd: i32, buf: [*]const u8, count: usize) isize {
62 |         // We only support writing to stderr or stdout
63 |         if (fd != std.os.STDERR_FILENO and fd != std.os.STDOUT_FILENO) {
64 |             errno = .PERM;
65 |             return -1;
66 |         }
67 | 
68 |         const clamped_count = @min(count, std.math.maxInt(isize));
69 |         writeToStderr(buf, clamped_count);
70 |         return @intCast(clamped_count);
71 |     }
72 | 
73 |     pub fn read(fd: i32, buf: [*]u8, count: usize) usize {
74 |         // We only support reading from stdin
75 |         if (fd != std.os.STDIN_FILENO) {
76 |             errno = .PERM;
77 |             return 0;
78 |         }
79 | 
80 |         const clamped_count = @min(count, std.math.maxInt(isize));
81 |         return @intCast(
82 |             readFromStdin(
83 |                 buf,
84 |                 clamped_count,
85 |             ),
86 |         );
87 |     }
88 | };
89 | 


--------------------------------------------------------------------------------
/tests/001-basic-types.buzz:
--------------------------------------------------------------------------------
 1 | import "std";
 2 | 
 3 | test "Basic types" {
 4 |     _ = "hello world";
 5 |     _ = 3.14;
 6 |     _ = 3;
 7 |     _ = true;
 8 | }
 9 | 
10 | test "Underscore on number literals" {
11 |     final a = 100_000;
12 | 
13 |     std\assert(a == 100000, message: "Could use an underscore on an int");
14 | 
15 |     final b = 3.1_4;
16 | 
17 |     std\assert(b == 3.14, message: "Could use an underscore on a double");
18 | 
19 |     final bin = 0b1_0001;
20 | 
21 |     std\assert(bin == 17, message: "Clould use an underscore on a binary int");
22 | 
23 |     final h = 0xF_FFF;
24 | 
25 |     std\assert(h == 65535, message: "Clould use an underscore on a hex int");
26 | }
27 | 
28 | test "Char literal" {
29 |     final char = 'A';
30 | 
31 |     std\assert(char == 65, message: "Could use char literal");
32 | 
33 |     final quote = '\'';
34 | 
35 |     std\assert(quote == 39, message: "Could escape ' in char literal");
36 | 
37 |     final slash = '\\';
38 | 
39 |     std\assert(slash == 92, message: "Could escape \\ in char literal");
40 | }
41 | 


--------------------------------------------------------------------------------
/tests/002-operators.buzz:
--------------------------------------------------------------------------------
 1 | import "std";
 2 | 
 3 | test "Binary operators" {
 4 |     std\assert(12 == 12, message: "equality (number)");
 5 |     std\assert(12 + 12 == 24, message: "addition");
 6 |     std\assert(12 - 12 == 0, message: "substraction");
 7 |     std\assert(12 * 12 == 144, message: "multiplication");
 8 |     std\assert(12 / 12 == 1, message: "division");
 9 |     std\assert(12 % 12 == 0, message: "modulo");
10 |     std\assert(12 != 13, message: "inequality");
11 |     std\assert(12 >= 12, message: "greater or equal");
12 |     std\assert(12 <= 12, message: "less or equal");
13 |     std\assert(12 > 11, message: "greater");
14 |     std\assert(12 < 13, message: "less");
15 |     std\assert(12 > 3 and 5 < 12, message: "and");
16 |     std\assert(12 > 3 or 12 < 5, message: "or");
17 | }
18 | 
19 | test "Binary operators for strings" {
20 |     std\assert("hello " + "world" == "hello world", message: "string concat");
21 |     std\assert("hello" == "hello", message: "equality (string");
22 | }
23 | 
24 | test "Unary operators (constant folded)" {
25 |     std\assert(-12 < 0, message: "negate operator");
26 |     std\assert(!false, message: "not operator");
27 |     std\assert(~15 == -16, message: "~");
28 | }
29 | 
30 | test "Unary operators" {
31 |     final a = 12;
32 |     final b = false;
33 |     final c = 15;
34 | 
35 |     std\assert(-a < 0, message: "negate operator");
36 |     std\assert(!b, message: "not operator");
37 |     std\assert(~c == -16, message: "~");
38 | }
39 | 


--------------------------------------------------------------------------------
/tests/003-control-flow.buzz:
--------------------------------------------------------------------------------
 1 | import "std";
 2 | 
 3 | test "if statement" {
 4 |     if (2 > 1) {
 5 |         std\assert(true, message: "if");
 6 |     } else {
 7 |         std\assert(false, message: "else");
 8 |     }
 9 | 
10 |     if (2 < 1) {
11 |         std\assert(false, message: "if");
12 |     } else {
13 |         std\assert(true, message: "else");
14 |     }
15 | }
16 | 
17 | test "if statement with placeholder" {
18 |     if (ahead == "wat") {
19 |         std\assert(true, message: "works with a placeholder");
20 |     } else {
21 |         std\assert(false, message: "if failed with placeholder");
22 |     }
23 | 
24 |     // if (objAhead.name == "joe") {
25 |     //     std\assert(true, message: "works with a placeholder");
26 |     // } else {
27 |     //     std\assert(false, message: "if failed with placeholder");
28 |     // }
29 | }
30 | 
31 | test "while statement" {
32 |     var i = 0;
33 |     while (i < 10) {
34 |         i = i + 1;
35 |     }
36 | 
37 |     std\assert(i == 10, message: "while");
38 | }
39 | 
40 | // test "while statement with placeholder" {
41 | //     while (objAhead.age < 10) {
42 | //         objAhead.age = objAhead.age + 1;
43 | //     }
44 | 
45 | //     std\assert(objAhead.age == 10, message: "while with placeholder");
46 | // }
47 | 
48 | 
49 | test "do-until statement" {
50 |     var i = 10;
51 |     do {
52 |         i = i - 1;
53 |     } until (i == 0)
54 | 
55 |     std\assert(i == 0, message: "do until");
56 | }
57 | 
58 | final ahead = "wat";
59 | 
60 | // object Ahead {
61 | //     str name = "joe",
62 | //     int age = 0
63 | // }
64 | 
65 | // Ahead objAhead = Ahead{};
66 | 


--------------------------------------------------------------------------------
/tests/004-lists.buzz:
--------------------------------------------------------------------------------
 1 | import "std";
 2 | 
 3 | test "Lists" {
 4 |     final list = [ 1, 2, 3, 4 ];
 5 | 
 6 |     std\assert(list.len() == 4, message: "len");
 7 | 
 8 |     final strList = mut [ "hello", "world" ];
 9 |     std\assert(strList[0] == "hello", message: "subscript");
10 | 
11 |     // A lone list expression
12 |     _ = [ "hello", "world" ];
13 |     _ = [ [ "hello" ], [ "world" ] ];
14 | 
15 |     final nestedList = [ [ "hello" ], [ "world" ] ];
16 | 
17 |     std\assert(nestedList[0][0] == "hello", message: "nested list");
18 | 
19 |     strList[1] = "yolo";
20 |     std\assert(strList[1] == "yolo", message: "list assignment");
21 | 
22 |     strList.append("dojo");
23 |     std\assert(strList[strList.len() - 1] == "dojo", message: "append to list");
24 | 
25 |     final removed = strList.remove(1);
26 |     std\assert(strList.len() == 2, message: "removed element form list");
27 |     std\assert(strList[0] == "hello" and strList[1] == "dojo", message: "item were properly shifted");
28 |     std\assert(removed == "yolo", message: "removed element has the correct value");
29 | 
30 |     std\assert(strList.remove(12) == null, message: "returns null when removing non existent index");
31 | }
32 | 
33 | test "list.sub" {
34 |     final list = [ 1, 2, 3, 4 ];
35 |     final sub = list.sub(1, len: 2);
36 | 
37 |     std\assert(sub.len() == 2 and sub[0] == 2 and sub[1] == 3, message: "list.sub");
38 | }
39 | 
40 | test "list.indexOf" {
41 |     std\assert([ 0, 1, 2, 3 ].indexOf(2) == 2, message: "list.indexOf");
42 | }
43 | 
44 | test "list.join" {
45 |     std\assert([ 1, 2, 3, 4 ].join(",") == "1,2,3,4", message: "list.join");
46 | }
47 | 
48 | test "list concat" {
49 |     std\assert(([ 1, 2, 3 ] + [ 4, 5, 6 ]).join(",") == "1,2,3,4,5,6", message: "list concat");
50 | }
51 | 
52 | test "list.clone" {
53 |     final list = [ 1, 2, 3 ];
54 |     final copy = list.copyImmutable();
55 | 
56 |     std\assert(list.len() == copy.len(), message: "Could clone list");
57 |     foreach (i, el in copy) {
58 |         std\assert(list[i] == el, message: "Could clone list");
59 |     }
60 | }
61 | 
62 | test "empty list type inferring" {
63 |     final list = [];
64 | 
65 |     std\assert(typeof list == <[any]>);
66 | 
67 |     final slist: [str] = [];
68 | 
69 |     std\assert(typeof slist == <[str]>);
70 | }
71 | 
72 | test "list.fill" {
73 |     final list = (mut [ 1, 2, 3 ]).fill(42);
74 | 
75 |     std\assert(list[0] == 42 and list[1] == 42 and list[2] == 42);
76 | 
77 |     final another = (mut [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]).fill(42, start: 2, len: 3);
78 | 
79 |     std\assert(another[2] == 42 and another[3] == 42 and another[4] == 42 and another[1] != 42 and another[5] != 42);
80 | }
81 | 


--------------------------------------------------------------------------------
/tests/005-maps.buzz:
--------------------------------------------------------------------------------
 1 | import "std";
 2 | 
 3 | test "Maps" {
 4 |     final map = mut {
 5 |         "hello": 1,
 6 |         "bye": 2,
 7 |     };
 8 | 
 9 |     _ = { 1: true, 2: false };
10 | 
11 |     std\assert(map["bye"] is int?, message: "yeah");
12 | 
13 |     std\assert(map["bye"] == 2, message: "map subscript");
14 |     std\assert(({ 1: true, 2: false })[2] == false, message: "map expression subscript");
15 | 
16 |     std\assert(map.remove("hello") == 1, message: "removed element");
17 |     // std\assert(map["hello"] == null, message: "removed element");
18 |     std\assert(map.size() == 1, message: "map size");
19 | }
20 | 
21 | test "map merge" {
22 |     final map = { "one": 1, "two": 22 } + { "three": 3, "two": 2 };
23 | 
24 |     std\assert(map["two"] == 2, message: "map merge");
25 |     std\assert(map.size() == 3, message: "map merge");
26 | }
27 | 
28 | test "map.keys" {
29 |     std\assert({ "one": 1, "two": 2, "three": 3 }.keys().join(",") == "one,two,three", message: "map.keys");
30 | }
31 | 
32 | test "map.values" {
33 |     std\assert({ "one": 1, "two": 2, "three": 3 }.values().join(",") == "1,2,3", message: "map.values");
34 | 
35 |     std\assert({}.keys().len() == 0, message: "yo empty map");
36 | }
37 | 
38 | test "map.diff" {
39 |     final first = {
40 |         "one": 1,
41 |         "two": 2,
42 |         "three": 3,
43 |     };
44 | 
45 |     final second = {
46 |         "two": 22,
47 |         "three": 33,
48 |         "four": 4,
49 |     };
50 | 
51 |     final diff = first.diff(second);
52 | 
53 |     std\assert(diff.size() == 1 and diff["one"] != null, message: "Could use map.diff");
54 | }
55 | 
56 | test "map.intersect" {
57 |     final first = {
58 |         "one": 1,
59 |         "two": 2,
60 |         "five": 5,
61 |     };
62 | 
63 |     final second = {
64 |         "two": 22,
65 |         "three": 33,
66 |         "four": 4,
67 |     };
68 | 
69 |     final intersect = first.intersect(second);
70 | 
71 |     std\assert(intersect.size() == 1 and intersect["two"] != null, message: "Could use map.intersect");
72 | }
73 | 
74 | test "map.clone" {
75 |     final first = {
76 |         "one": 1,
77 |         "two": 2,
78 |         "five": 5,
79 |     };
80 |     final copy = first.copyImmutable();
81 | 
82 |     std\assert(copy.size() == first.size(), message: "Could clone map");
83 |     foreach (key, value in copy) {
84 |         std\assert(first[key] == value, message: "Could clone map");
85 |     }
86 | }
87 | 
88 | test "empty map type inferring" {
89 |     final map = {};
90 | 
91 |     std\assert(typeof map == <{any: any}>);
92 | 
93 |     final smap: {str: int} = {};
94 | 
95 |     std\assert(typeof smap == <{str: int}>);
96 | }
97 | 


--------------------------------------------------------------------------------
/tests/006-enums.buzz:
--------------------------------------------------------------------------------
 1 | import "std";
 2 | 
 3 | enum StrEnum {
 4 |     one,
 5 |     two,
 6 |     three,
 7 | }
 8 | 
 9 | enum IntEnum {
10 |     one = 1,
11 |     two = 2,
12 |     three = 3,
13 |     // wat = "wat",
14 | }
15 | 
16 | enum NaturalEnum {
17 |     zero,
18 |     one,
19 |     two,
20 | }
21 | 
22 | fun getValue(case: NaturalEnum = NaturalEnum.zero) > int {
23 |     return case.value;
24 | }
25 | 
26 | object Natural {
27 |     natural: NaturalEnum = NaturalEnum.zero,
28 | }
29 | 
30 | test "Enums" {
31 |     std\assert(StrEnum.one.value == "one", message: "str enum");
32 | 
33 |     std\assert(IntEnum.one.value == 1, message: "int enum");
34 | 
35 |     std\assert(NaturalEnum.zero.value == 0, message: "natural enum");
36 | 
37 |     final myCase = NaturalEnum.two;
38 | 
39 |     std\assert(myCase.value == 2, message: "enum instance");
40 | 
41 |     final fromValue = NaturalEnum(0);
42 |     std\assert(fromValue != null, message: "Could get enum instance from value");
43 |     std\assert(fromValue?.value == 0, message: "Could get correct enum instance from value");
44 | }
45 | 
46 | test "Enum case as default value" {
47 |     std\assert(getValue() == 0, message: "Could use enum case as function argument default value");
48 | 
49 |     std\assert(Natural{}.natural == NaturalEnum.zero, message: "Could use enum case as object field default value");
50 | }
51 | 


--------------------------------------------------------------------------------
/tests/007-objects.buzz:
--------------------------------------------------------------------------------
 1 | import "std";
 2 | 
 3 | object First {
 4 |     name: str = "Joe",
 5 |     age: int = 10,
 6 | 
 7 |     fun sayHello() > void {
 8 |         std\print("hello");
 9 |         std\print(this.name);
10 |     }
11 | }
12 | 
13 | test "Objects" {
14 |     final first = mut First{
15 |         name = "John",
16 |     };
17 | 
18 |     std\assert(first.name == "John", message: "object instance, field access");
19 | 
20 |     first.age = 21;
21 |     std\assert(first.age == 21, message: "field assignment");
22 | 
23 |     first.sayHello();
24 | }
25 | 
26 | object Second {
27 |     static nextId: int = -1;
28 |     id: int,
29 | 
30 |     static fun init() > Second {
31 |         Second.nextId = Second.nextId + 1;
32 | 
33 |         return Second{
34 |             id = Second.nextId,
35 |         };
36 |     }
37 | }
38 | 
39 | test "Object with static fields" {
40 |     final second = Second.init();
41 | 
42 |     std\assert(second.id == Second.nextId, message: "could use static fields");
43 | }
44 | 


--------------------------------------------------------------------------------
/tests/008-inline-catch.buzz:
--------------------------------------------------------------------------------
 1 | import "std";
 2 | 
 3 | fun willFail() > int !> str {
 4 |     throw "i'm failing";
 5 |     return 1;
 6 | }
 7 | 
 8 | fun willFailVoid() > void !> str {
 9 |     throw "i'm failing";
10 | }
11 | 
12 | test "Inline catch clauses" {
13 |     std\assert((willFail() catch 0) == 0, message: "error or default value");
14 | }
15 | 
16 | test "Inline catch void" {
17 |     willFailVoid() catch void;
18 | }
19 | 


--------------------------------------------------------------------------------
/tests/009-gc.buzz:
--------------------------------------------------------------------------------
 1 | import "std";
 2 | import "gc";
 3 | 
 4 | var collectorCalled = false;
 5 | 
 6 | object Kill {
 7 |     yo: int = -1,
 8 | 
 9 |     fun collect() > void {
10 |         collectorCalled = true;
11 |     }
12 | }
13 | 
14 | test "GC, collecting unreferenced objects" {
15 |     var i = 0;
16 |     _ = Kill{}; // Should be kept longer
17 |     while (i < 1000) {
18 |         _ = Kill{ yo = i }; // Should be collected since not referenced anywhere
19 | 
20 |         i = i + 1;
21 |     }
22 | 
23 |     final before = gc\allocated();
24 | 
25 |     gc\collect();
26 | 
27 |     std\assert(gc\allocated() <= before, message: "Garbage was collected");
28 |     std\assert(collectorCalled, message: "Object collector was called");
29 | }
30 | 


--------------------------------------------------------------------------------
/tests/010-placeholder-cycle.buzz:
--------------------------------------------------------------------------------
 1 | import "std";
 2 | 
 3 | object A {
 4 |     b: B,
 5 | }
 6 | 
 7 | object B {
 8 |     a: A,
 9 | }
10 | 
11 | object Node {
12 |     another: Node,
13 | 
14 |     fun something() > Node {
15 |         return this.another.somethingelse();
16 |     }
17 | 
18 |     fun somethingelse() > Node {
19 |         return this.another;
20 |     }
21 | }
22 | 
23 | test "Cyclic placeholders" {
24 |     std\print("{A}");
25 |     std\print("{B}");
26 | }
27 | 


--------------------------------------------------------------------------------
/tests/011-list-map-properties.buzz:
--------------------------------------------------------------------------------
 1 | import "std";
 2 | 
 3 | object Hey {
 4 |     ages: [int],
 5 |     names: {int: str},
 6 | }
 7 | 
 8 | test "List and Map as Object properties" {
 9 |     final hey = Hey{
10 |         ages = [ 1, 2, 3 ],
11 |         names = { 1: "hello" },
12 |     };
13 | 
14 |     std\assert(hey.ages[0] == 1, message: "list access");
15 |     std\assert(hey.names[1] == "hello", message: "map access");
16 | }
17 | 


--------------------------------------------------------------------------------
/tests/012-lambda.buzz:
--------------------------------------------------------------------------------
 1 | import "std";
 2 | 
 3 | test "Lambda/Anonymous functions" {
 4 |     final mul: fun (n: int) > int = fun (n: int) > int => n * 2;
 5 | 
 6 |     std\assert(mul(1) == 2, message: "called a lambda function");
 7 | }
 8 | 
 9 | fun callThis(fn: fun (n: int) > int, arg: int) > int {
10 |     return fn(arg);
11 | }
12 | 
13 | test "Function as arguments" {
14 |     std\assert(
15 |         (fun (n: int) > int {
16 |             return n * n;
17 |         })(10) == 100,
18 |         message: "called anonymous function",
19 |     );
20 |     std\assert((fun (n: int) > int => n * n)(10) == 100, message: "called lambda function");
21 |     std\assert(callThis(fun (n: int) > int => n * 2, arg: 2) == 4, message: "called a function from a function");
22 | }
23 | 
24 | fun mul(a: int, b: int) > int => a * b;
25 | 
26 | test "Any function can be an arrow function" {
27 |     std\assert(mul(a: 2, b: 2) == 4, message: "arrow function");
28 | }
29 | 


--------------------------------------------------------------------------------
/tests/013-import-export.buzz:
--------------------------------------------------------------------------------
 1 | import "tests/utils/testing" as testing;
 2 | 
 3 | final mine = 42;
 4 | 
 5 | test "Using a function coming from an import" {
 6 |     final me = testing\PrefixMe{};
 7 |     testing\assert(me.name == "Joe", message: "prefixed global works as type");
 8 |     testing\assert(testing\hey("world") == mine, message: "unexported symbol is reachable");
 9 |     testing\assert(true, message: "yeah!");
10 | }
11 | 


--------------------------------------------------------------------------------
/tests/014-import-lib.buzz:
--------------------------------------------------------------------------------
1 | import print, assert from "std";
2 | 
3 | test "Using a function coming from a library" {
4 |     std\assert(true, message: "yeah!");
5 |     std\print("wat");
6 | }
7 | 


--------------------------------------------------------------------------------
/tests/015-interpolation.buzz:
--------------------------------------------------------------------------------
 1 | import "std";
 2 | 
 3 | test "Escape sequences" {
 4 |     std\print("\{escaped interpolation}, \nhello\tworld, backslash \\ \"hey\"");
 5 | }
 6 | 
 7 | test "String interpolation" {
 8 |     final name = "joe";
 9 |     final age = 12;
10 | 
11 |     std\assert(
12 |         "{"$"} hello {name} i'm {age} years old {3 + 4}" == "$ hello joe i'm 12 years old 7",
13 |         message: "interpolation",
14 |     );
15 | 
16 |     std\assert(
17 |         "not starting with a {name} {age} yeah!" == "not starting with a joe 12 yeah!",
18 |         message: "interpolation order",
19 |     );
20 | 
21 |     // std\assert("\60\61\62" == "<=>", message: "raw char");
22 | }
23 | 
24 | test "Printing empty string" {
25 |     std\print("");
26 | }
27 | 


--------------------------------------------------------------------------------
/tests/016-optionals.buzz:
--------------------------------------------------------------------------------
 1 | import "std";
 2 | 
 3 | test "Optional force unwrapping with `!`" {
 4 |     // Note: trying to force unwrap a null value raises an uncatchable error
 5 |     // Dart allows to catch an null unwrap but swift doesn't
 6 |     // I think it's saner for it to not be catchable but to provides safe way to unwrap it
 7 |     final hello: str? = "hello world";
 8 | 
 9 |     std\assert(hello! == "hello world", message: "Could force unwrap an optional");
10 | }
11 | 
12 | test "Optional graceful unwrapping with `?`" {
13 |     final optList: [int]? = [ 1, 2, 3 ];
14 | 
15 |     std\assert(optList?.len() == 3, message: "could unwrap optList");
16 | }
17 | 
18 | object Me {
19 |     list: [int]?,
20 | }
21 | 
22 | test "Optional chaining" {
23 |     final me: Me? = Me{
24 |         list = [ 1, 2, 3 ],
25 |     };
26 | 
27 |     std\assert(me?.list?.len() == 3, message: "chaining optionals work");
28 | 
29 |     final you: Me? = null;
30 | 
31 |     std\assert(you?.list?.len() == null, message: "chaining optionals work");
32 | }
33 | 
34 | test "Null coalescing operator" {
35 |     final hello: str? = null;
36 | 
37 |     std\assert(hello ?? "world" == "world", message: "null coalescing");
38 | }
39 | 
40 | test "Unwrap map subscript" {
41 |     final map = {
42 |         "yo": "lo",
43 |         "mo": "jo",
44 |     };
45 | 
46 |     std\assert(map["yo"]?.len() == 2, message: "could unwrap map subscript");
47 | }
48 | 
49 | object You {
50 |     name: str = "joe",
51 | }
52 | 
53 | test "Field access on map subscript" {
54 |     final map = {
55 |         "yo": You{},
56 |     };
57 | 
58 |     std\assert(map["yo"]?.name == "joe", message: "could field access map subscript");
59 | }
60 | 
61 | object A {
62 |     static instance: A?;
63 |     msg: str,
64 | 
65 |     fun hello() > str {
66 |         return this.msg;
67 |     }
68 | }
69 | 
70 | test "Check ahead" {
71 |     A.instance = A{ msg = "hello" };
72 | 
73 |     _ = A.instance?.hello();
74 | }
75 | 
76 | object AA {
77 |     data: int,
78 | 
79 |     fun eql(other: AA?) > bool {
80 |         return this.data == other?.data;
81 |     }
82 | }
83 | 
84 | object BB {
85 |     data: AA?,
86 | 
87 |     fun eql(other: BB?) > bool? {
88 |         return this.data?.eql(other?.data);
89 |     }
90 | }
91 | 


--------------------------------------------------------------------------------
/tests/017-for.buzz:
--------------------------------------------------------------------------------
 1 | import "std";
 2 | 
 3 | test "for loop" {
 4 |     var sum = 0;
 5 |     for (i: int = 0; i < 10; i = i + 1) {
 6 |         sum = sum + i;
 7 |     }
 8 | 
 9 |     std\assert(sum == 45, message: "for loop");
10 | }
11 | 
12 | test "multiple variable and expressions in for loop" {
13 |     var sum = 0;
14 |     for (i: int = 0, j: int = 9; i < 10 and j >= 0; i = i + 1, j = j - 1) {
15 |         sum = sum + i + j;
16 |     }
17 | 
18 |     std\assert(sum == 90, message: "multiple var loop");
19 | }
20 | 


--------------------------------------------------------------------------------
/tests/018-foreach.buzz:
--------------------------------------------------------------------------------
 1 | import "std";
 2 | 
 3 | test "foreach on list" {
 4 |     final list = [ 1, 2, 3 ];
 5 | 
 6 |     var sum = 0;
 7 |     foreach (item in list) {
 8 |         sum = sum + item;
 9 |     }
10 | 
11 |     std\assert(sum == 6, message: "foreach on list");
12 | }
13 | 
14 | test "list.next" {
15 |     final list = [ 1, 2, 3 ];
16 | 
17 |     std\assert(list.next(null) == 0, message: "calling next native");
18 |     std\assert(list.next(0) == 1, message: "calling next native");
19 | }
20 | 
21 | test "foreach on map" {
22 |     final map = {
23 |         "one": 1,
24 |         "two": 2,
25 |         "three": 3,
26 |     };
27 | 
28 |     var sum = 0;
29 |     foreach (value in map) {
30 |         sum = sum + value;
31 |     }
32 | 
33 |     std\assert(sum == 6, message: "foreach on map");
34 | }
35 | 
36 | enum Hey {
37 |     one,
38 |     two,
39 |     three,
40 | }
41 | 
42 | test "foreach on enum" {
43 |     var sum = 0;
44 |     foreach (case in Hey) {
45 |         sum = sum + case.value;
46 |     }
47 | 
48 |     std\assert(sum == 3, message: "foreach on enum");
49 | }
50 | 
51 | test "foreach on str" {
52 |     var hello = "";
53 |     foreach (char in "hello world") {
54 |         hello = "{hello}{char}";
55 |     }
56 | 
57 |     std\assert(hello == "hello world", message: "foreach on str");
58 | }
59 | 
60 | test "Omit key in foreach" {
61 |     var sum = 0;
62 |     foreach (n in [ 1, 2, 3 ]) {
63 |         sum = sum + n;
64 |     }
65 |     std\assert(sum == 6, message: "Could omit list key");
66 | 
67 |     var hello = "";
68 |     foreach (char in "hello") {
69 |         hello = "{hello}{char}";
70 |     }
71 |     std\assert(hello == "hello", message: "Could omit string key");
72 | 
73 |     sum = 0;
74 |     foreach (n in { "hello": 1, "world": 2 }) {
75 |         sum = sum + n;
76 |     }
77 |     std\assert(sum == 3, message: "Could omit map key");
78 | }
79 | 


--------------------------------------------------------------------------------
/tests/019-is.buzz:
--------------------------------------------------------------------------------
 1 | import "std";
 2 | 
 3 | object MyObj {
 4 |     id: int = 1,
 5 | 
 6 |     fun bound() > bool {
 7 |         return true;
 8 |     }
 9 | }
10 | 
11 | enum MyEnum {
12 |     one,
13 |     two,
14 |     three,
15 | }
16 | 
17 | fun myFun(id: int) > int {
18 |     return id * 2;
19 | }
20 | 
21 | test "`is` operator" {
22 |     std\assert(12 is int, message: "`is` on int");
23 |     std\assert(true is bool, message: "`is` on bool");
24 |     std\assert("yo" is str, message: "`is` on str");
25 | 
26 |     std\assert(MyObj{} is MyObj, message: "`is` for an object instance");
27 |     std\assert(MyEnum.one is MyEnum, message: "`is` for an enum instance");
28 |     std\assert(myFun is fun (id: int) > int, message: "`is` for a function");
29 | 
30 |     std\assert([ 1, 2, 3 ] is [int], message: "`is` on a list");
31 |     std\assert({ "one": 1 } is {str: int}, message: "`is` on a map");
32 |     // TODO: should be `fun (MyObj) > bool`
33 |     std\assert(MyObj{}.bound is fun () > bool, message: "`is` on bound method");
34 | }
35 | 


--------------------------------------------------------------------------------
/tests/021-upvalues.buzz:
--------------------------------------------------------------------------------
 1 | import "std";
 2 | 
 3 | fun upvals() > fun () {
 4 |     final upvalue = 12;
 5 |     final up = "up";
 6 | 
 7 |     return fun () > void => std\print("{upvalue} {up}");
 8 | }
 9 | 
10 | test "Upvalues" {
11 |     upvals()();
12 | }
13 | 


--------------------------------------------------------------------------------
/tests/022-io.buzz:
--------------------------------------------------------------------------------
 1 | import "std";
 2 | import "io";
 3 | import "fs";
 4 | 
 5 | test "Write & read a file" {
 6 |     final file = io\File.open("./hello.txt", mode: io\FileMode.write);
 7 | 
 8 |     file.write("Hello World");
 9 | 
10 |     file.close();
11 | 
12 |     // Now read it
13 |     final fileB = io\File.open("./hello.txt", mode: io\FileMode.read);
14 | 
15 |     std\assert(file.readAll() == "Hello World", message: "Could write and read a file");
16 | 
17 |     fileB.close();
18 | 
19 |     fs\delete("./hello.txt");
20 | }
21 | 
22 | test "Write on stdout" {
23 |     io\stdout.write("Hello World !\n");
24 | }
25 | 
26 | test "Read by lines" {
27 |     final file = io\File.open("./README.md", mode: io\FileMode.read);
28 | 
29 |     for (lines: int = 0, line: str? = ""; line != null; line = file.readLine(), lines = lines + 1) {
30 |         std\print("{lines}: {line}");
31 |     }
32 | 
33 |     file.close();
34 | }
35 | 
36 | test "Read" {
37 |     final file = io\File.open("./README.md", mode: io\FileMode.read);
38 | 
39 |     std\assert(file.read(18) == "

", message: "Can read n bytes"); 40 | 41 | file.close(); 42 | } 43 | -------------------------------------------------------------------------------- /tests/023-std.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | test "std\parseInt/Double" { 4 | std\assert(std\parseInt("12") == 12, message: "Could parse int"); 5 | std\assert(std\parseDouble("12.42") == 12.42, message: "Could parse double"); 6 | std\assert(std\parseInt("not a number") == null, message: "Doesn't parse stupid shit"); 7 | 8 | std\assert(std\toInt(23.34) == 23, message: "Could cast double to int"); 9 | std\assert(std\toDouble(23) == 23.0, message: "Could cast int to double"); 10 | std\assert(std\toUd(23) is ud, message: "Could cast int to ud"); 11 | std\assert(std\toUd(23.0) is ud, message: "Could cast double to ud"); 12 | 13 | std\assert(std\parseUd("42") == std\toUd(42), message: "Could parse ud"); 14 | } 15 | 16 | test "char" { 17 | std\assert(std\char(65) == "A", message: "char"); 18 | } 19 | 20 | test "random" { 21 | std\assert(std\random(min: 5, max: 10) >= 5, message: "random range"); 22 | std\assert(std\random(max: 10) <= 10, message: "random range"); 23 | } 24 | -------------------------------------------------------------------------------- /tests/024-os.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | import "os" as os; 3 | 4 | test "os\env" { 5 | std\assert(os\env("HOME") != null, message: "could get env variable"); 6 | } 7 | 8 | test "os\time" { 9 | std\assert(os\time() > 0, message: "Got time"); 10 | } 11 | 12 | test "os\tmpDir" { 13 | // TODO: replace by .len check 14 | std\assert(os\tmpDir() != "", message: "Got system tmp dir"); 15 | } 16 | 17 | test "os\tmpFilename" { 18 | std\assert(os\tmpFilename("buzz_test") != "", message: "Got tmp file name"); 19 | } 20 | 21 | test "os\execute" { 22 | std\assert(os\execute([ "./zig-out/bin/buzz", "--version" ]) == 0, message: "Could execute a command"); 23 | } 24 | 25 | test "os\sleep" { 26 | os\sleep(500.0); 27 | } 28 | -------------------------------------------------------------------------------- /tests/025-fs.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | import "fs" as fs; 3 | 4 | test "fs\cwd" { 5 | std\assert(fs\currentDirectory() != "", message: "Could get cwd"); 6 | } 7 | 8 | test "mkDir/rm" { 9 | fs\makeDirectory("yo"); 10 | fs\makeDirectory("{fs\currentDirectory()}/yo2"); 11 | 12 | fs\delete("yo"); 13 | fs\delete("{fs\currentDirectory()}/yo2"); 14 | } 15 | 16 | test "ls" { 17 | var containsREADME = false; 18 | foreach (el in fs\list(fs\currentDirectory())) { 19 | _ = "hello there!"; 20 | if (el == "README.md") { 21 | containsREADME = true; 22 | break; 23 | } 24 | } 25 | 26 | final anotherRandomLocal = "bye there!"; 27 | 28 | std\assert(anotherRandomLocal == "bye there!", message: "foreach break is wrong"); 29 | 30 | std\assert(containsREADME, message: "Could list element in current directory"); 31 | 32 | foreach (el in fs\list("/doesnotexist") catch [ "wentwrong" ]) { 33 | std\assert(el == "wentwrong", message: "Trying to list a non existent directory raised an error"); 34 | } 35 | 36 | std\assert(fs\exists("README.md"), message: "fs\\exists"); 37 | } 38 | 39 | test "move" { 40 | fs\move(source: "README.md", destination: "src/README.md"); 41 | 42 | std\assert( 43 | fs\list("src").indexOf("README.md") != null, 44 | message: "Moved file to expected location", 45 | ); 46 | 47 | fs\move(source: "src/README.md", destination: "README.md"); 48 | 49 | std\assert( 50 | fs\list(".").indexOf("README.md") != null, 51 | message: "Moved file to expected location", 52 | ); 53 | } 54 | -------------------------------------------------------------------------------- /tests/026-break-continue.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | test "break statement" { 4 | var i = 0; 5 | while (i < 10) { 6 | i = i + 1; 7 | 8 | if (i == 3) { 9 | break; 10 | } 11 | } 12 | 13 | std\assert(i == 3, message: "break"); 14 | } 15 | 16 | test "continue statement" { 17 | var i = 0; 18 | while (i < 10) { 19 | i = i + 1; 20 | 21 | if (i == 3) { 22 | continue; 23 | } 24 | 25 | i = i + 1; 26 | } 27 | 28 | std\assert(i == 11, message: "break"); 29 | } 30 | -------------------------------------------------------------------------------- /tests/027-run-file.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | import "io"; 3 | 4 | test "runFile" { 5 | io\runFile("tests/utils/testing.buzz"); 6 | 7 | std\assert(true, message: "Could run a buzz file"); 8 | } 9 | 10 | test "run non existent file" { 11 | var errorRaised = false; 12 | try { 13 | io\runFile("tests/utils/testingsldkfj.buzz"); 14 | } catch { 15 | errorRaised = true; 16 | } 17 | 18 | std\assert(errorRaised, message: "Non existent file raised an error"); 19 | } 20 | -------------------------------------------------------------------------------- /tests/028-math.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | import "math" as math; 3 | 4 | test "math" { 5 | std\assert(math\abs(-12.234) == 12.234, message: "math\\abs"); 6 | std\assert(math\acos(0.1) == 1.4706289056333368, message: "math\\acos"); 7 | std\assert(math\asin(0.1) == 0.1001674211615598, message: "math\\asin"); 8 | std\assert(math\atan(0.1) == 0.09966865249116204, message: "math\\atan"); 9 | std\assert(math\ceil(12.234) == 13, message: "math\\ceil"); 10 | std\assert(math\cos(12.234) == 0.9452715049027691, message: "math\\cos"); 11 | std\assert(math\exp(12.234) == 205664.19575705, message: "math\\exp"); 12 | std\assert(math\floor(12.234) == 12, message: "math\\floor"); 13 | std\assert(math\sin(12.234) == -0.3262848173281347, message: "math\\sin"); 14 | std\assert(math\sqrt(12.234) == 3.4977135388707863, message: "math\\sqrt"); 15 | std\assert(math\tan(12.234) == -0.34517576763481983, message: "math\\tan"); 16 | 17 | std\assert(math\minInt(a: 12, b: 124) == 12, message: "math\\min"); 18 | std\assert(math\maxInt(a: 12, b: 124) == 124, message: "math\\max"); 19 | 20 | std\assert(math\deg(2.0) == 114.59155902616439, message: "math\\deg"); 21 | std\assert(math\rad(math\deg(2.0)) == 2, message: "math\\rad"); 22 | } 23 | -------------------------------------------------------------------------------- /tests/029-default-arguments.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | fun hey(name: str = "Joe", age: int = 12, father: str?, fourth: int = 1) > str => "Hello {name} you're {age} {father} {fourth}"; 4 | 5 | test "function default arguments" { 6 | std\assert(hey("John") == "Hello John you're 12 null 1", message: "Could reorder or omit argument"); 7 | std\assert(hey(age: 25) == "Hello Joe you're 25 null 1", message: "Could reorder or omit argument"); 8 | std\assert(hey(father: "Doe") == "Hello Joe you're 12 Doe 1", message: "Could reorder or omit argument"); 9 | std\assert(hey(fourth: 42) == "Hello Joe you're 12 null 42", message: "Could reorder or omit argument"); 10 | std\assert(hey(fourth: 12, age: 44) == "Hello Joe you're 44 null 12", message: "Could reorder or omit argument"); 11 | } 12 | -------------------------------------------------------------------------------- /tests/030-str.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | test "str subscript" { 4 | std\assert("hello world"[1] == "e", message: "str subscript"); 5 | } 6 | 7 | test "str.len" { 8 | std\assert("hello world".len() == 11, message: "str.len"); 9 | } 10 | 11 | test "str.byte" { 12 | std\assert("hello world".byte(1) == 101, message: "str.byte"); 13 | } 14 | 15 | test "str.indexOf" { 16 | std\assert("hello world".indexOf("world") == 6, message: "str.indexOf"); 17 | std\assert("hello world".indexOf("moon") == null, message: "str.indexOf"); 18 | } 19 | 20 | test "str.split" { 21 | final splits = "one,two,three".split(","); 22 | 23 | std\assert(splits[0] == "one" and splits[1] == "two" and splits[2] == "three", message: "str.split"); 24 | } 25 | 26 | test "str.sub" { 27 | std\assert("hello world".sub(6) == "world", message: "str.sub"); 28 | std\assert("hello world".sub(0, len: 5) == "hello", message: "str.sub"); 29 | } 30 | 31 | test "base64" { 32 | std\assert("hello world".encodeBase64() == "aGVsbG8gd29ybGQ=", message: "could encode in b64"); 33 | std\assert("aGVsbG8gd29ybGQ=".decodeBase64() == "hello world", message: "could encode in b64"); 34 | } 35 | 36 | test "upper/lower" { 37 | std\assert("hello world!".upper() == "HELLO WORLD!", message: "upper"); 38 | std\assert("HellO WorlD!".lower() == "hello world!", message: "lower"); 39 | } 40 | 41 | test "hex/bin" { 42 | std\assert("c3fcd3d76192e4007dfb496cca67e13b".bin().hex() == "c3fcd3d76192e4007dfb496cca67e13b", message: "hex/bin"); 43 | } 44 | 45 | test "trim" { 46 | std\assert(" hello world \t\n".trim() == "hello world", message: "could trim str"); 47 | } 48 | -------------------------------------------------------------------------------- /tests/031-json.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | import "serialize"; 3 | 4 | test "Json.encode" { 5 | final data = { 6 | "hello": "world", 7 | "bye": 42, 8 | }; 9 | 10 | std\assert( 11 | serialize\jsonEncode(serialize\Boxed.init(data)) == `\{"hello":"world","bye":42}`, 12 | message: "valid encode", 13 | ); 14 | } 15 | 16 | test "Json.decode" { 17 | std\assert( 18 | serialize\jsonDecode(`[ -12, true, "hello" ]`).listValue()[2].string() == "hello", 19 | message: "could decode simple JSON", 20 | ); 21 | } 22 | 23 | test "Boxed.q" { 24 | final data = { 25 | "submap": { 26 | , 27 | "subsubmap": { 28 | , 29 | "one": 1, 30 | "two": 2, 31 | }, 32 | }, 33 | }; 34 | 35 | final boxed = serialize\Boxed.init(data); 36 | 37 | std\assert(boxed.q([ "submap", "subsubmap", "one" ]).integer() == 1, message: "Boxed.q"); 38 | } 39 | -------------------------------------------------------------------------------- /tests/032-debug.buzz: -------------------------------------------------------------------------------- 1 | import "debug"; 2 | 3 | enum MyEnum { 4 | One, 5 | Two, 6 | Three, 7 | } 8 | 9 | enum MyStringEnum { 10 | One, 11 | Two, 12 | Three, 13 | } 14 | 15 | object Data { 16 | data: [int], 17 | } 18 | 19 | object MyObject { 20 | name: str, 21 | age: int = 12, 22 | data: Data, 23 | 24 | static fun init(name: str, age: int) > MyObject { 25 | return MyObject{ 26 | name = name, 27 | age = age, 28 | data = Data{ data = [] }, 29 | }; 30 | } 31 | } 32 | 33 | test "dump" { 34 | final list = [ 1, 2, 3, 4 ]; 35 | final map = { 36 | "one": 1, 37 | "two": 2, 38 | "three": 3, 39 | }; 40 | 41 | final instance = MyObject{ 42 | name = "joe", 43 | age = 35, 44 | data = Data{ 45 | data = list, 46 | }, 47 | }; 48 | 49 | debug\dump(list); 50 | debug\dump(map); 51 | debug\dump(instance); 52 | debug\dump(MyStringEnum.One); 53 | debug\dump(MyEnum.One); 54 | debug\dump("hello world"); 55 | debug\dump($"hello .*"); 56 | } 57 | -------------------------------------------------------------------------------- /tests/033-invoke.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | object A { 4 | list: [A], 5 | 6 | fun hello() > A { 7 | std\print("hello"); 8 | return this; 9 | } 10 | } 11 | 12 | test "Chained invoke" { 13 | final a = A{ 14 | list = [ 15 | A{ 16 | list = [ A{ list = [] } ], 17 | }, 18 | A{ 19 | list = [], 20 | }, 21 | ], 22 | }; 23 | 24 | _ = a.list[0].hello().list[0].hello(); 25 | } 26 | -------------------------------------------------------------------------------- /tests/034-scope.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | test "locals inside a foreach" { 4 | final hello = ""; 5 | 6 | foreach (_ in "hello world") { 7 | final new = "yo"; 8 | final old = "lo"; 9 | 10 | foreach (_ in "goodbye world") { 11 | final newnew = "yoyo"; 12 | final oldold = "lolo"; 13 | 14 | std\assert(new == "yo", message: "locals are ok"); 15 | std\assert(old == "lo", message: "locals are ok"); 16 | std\assert(newnew == "yoyo", message: "locals are ok"); 17 | std\assert(oldold == "lolo", message: "locals are ok"); 18 | } 19 | 20 | std\assert(hello == ""); 21 | } 22 | 23 | for (i: int = 0; i < 3; i = i + 1) { 24 | final new = "yo"; 25 | final old = "lo"; 26 | 27 | std\assert(new == "yo", message: "locals are ok"); 28 | std\assert(old == "lo", message: "locals are ok"); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/035-const-expr.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | fun hello( 4 | name: mut [str] = [ "John", "Doe" ], 5 | address: mut {str: str} = { 6 | "street": "somewhere street", 7 | "town": "New York", 8 | }, 9 | ) > void { 10 | std\assert(name.len() == 2, message: "default arg is clone of the default value"); 11 | std\assert(address.size() == 2, message: "default arg is clone of the default value"); 12 | 13 | std\print("Hello I'm {name[0]} {name[1]} I live at {address["street"]} in {address["town"]}"); 14 | 15 | name.append("Yolo"); 16 | address["country"] = "US"; 17 | } 18 | 19 | object A { 20 | list: mut [int] = mut [ 1, 2, 3 ], 21 | map: mut {str: int} = mut { "yo": 1 }, 22 | } 23 | 24 | test "Constant expression" { 25 | hello(); 26 | hello(); 27 | 28 | final a = A{}; 29 | final b = A{}; 30 | 31 | std\assert(a.list != b.list, message: "object default value were cloned"); 32 | std\assert(a.map != b.map, message: "object default value were cloned"); 33 | 34 | a.list.append(4); 35 | 36 | std\assert(a.list.len() == 4, message: "object default value were cloned"); 37 | std\assert(b.list.len() == 3, message: "object default value were cloned"); 38 | 39 | a.map["lo"] = 4; 40 | 41 | std\assert(a.map.size() == 2, message: "object default value were cloned"); 42 | std\assert(b.map.size() == 1, message: "object default value were cloned"); 43 | } 44 | -------------------------------------------------------------------------------- /tests/036-pattern.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | test "pattern.match" { 4 | final pattern = $"hello ([a-z]+)"; 5 | 6 | final results = pattern.match("All i want to say is hello joe! hello mundo!"); 7 | 8 | std\assert(results?.len() == 2, message: "1 match and 1 capture"); 9 | std\assert(results?[0].capture == "hello joe", message: "first is match"); 10 | std\assert(results?[1].capture == "joe", message: "second is capture"); 11 | } 12 | 13 | test "pattern.matchAll" { 14 | final pattern = $"hello ([a-z]+)"; 15 | 16 | final results = pattern.matchAll("All i want to say is hello joe!\nhello mundo!\nAnd hello neighbor..."); 17 | 18 | std\assert(results?.len() == 3, message: "found 3 matches"); 19 | std\assert(results![2].len() == 2, message: "1 match and 1 capture"); 20 | std\assert(results![2][1].capture == "neighbor", message: "capture is correct"); 21 | } 22 | 23 | test "Escaped pattern delimiter" { 24 | final pattern = $"hello \" world"; 25 | 26 | std\assert("{pattern}" == "hello \" world", message: "delimiter was properly escaped"); 27 | } 28 | 29 | test "replace" { 30 | std\assert( 31 | $"world".replace("All i want to say is hello world!", with: "mundo") == "All i want to say is hello mundo!", 32 | message: "replace", 33 | ); 34 | 35 | std\assert( 36 | $"alright".replaceAll("he says: alright, alright, alright!", with: "check") == "he says: check, check, check!", 37 | message: "replaceAll", 38 | ); 39 | } 40 | -------------------------------------------------------------------------------- /tests/037-dead-branches.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | test "if" { 4 | if (true) { 5 | std\assert(true, message: "only this branch should be generated"); 6 | } else { 7 | std\assert(false, message: "unreachable"); 8 | } 9 | } 10 | 11 | test "foreach" { 12 | foreach (_ in {}) { 13 | std\assert(false, message: "unreachable"); 14 | } 15 | } 16 | 17 | test "while" { 18 | while (false) { 19 | std\assert(false, message: "unreachable"); 20 | } 21 | } 22 | 23 | test "for" { 24 | for (i: int = 0; false; i = i + 1) { 25 | std\assert(false, message: "unreachable"); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/038-fibers.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | test "fiber" { 4 | // Async call, creates a new fiber, yield type must be nullable because resume will return null when nothing yielded 5 | final counter = &count(10); 6 | 7 | // A fiber is over when a OP_RETURN as been executed 8 | var sum = 0; 9 | while (!counter.over()) { 10 | // resume returns null if nothing was yielded and/or fiber is over 11 | sum = sum + resume counter ?? 0; 12 | } 13 | 14 | std\assert(counter.over(), message: "Fiber should be over now"); 15 | 16 | // resolve runs fiber until it's over and dismiss any yielded value along the way 17 | std\assert(resolve counter == "Counting is done!", message: "Fiber is over we get its return value"); 18 | std\assert(sum == 45, message: "Sum is good"); 19 | 20 | std\assert(resolve &count(10) == "Counting is done!", message: "Resolve without any resume"); 21 | 22 | std\assert(std\currentFiber().isMain(), message: "We recognize main fiber"); 23 | } 24 | 25 | /// returns str, yields int 26 | fun count(n: int) > str *> int? { 27 | std\assert(std\currentFiber() is fib, message: "Can get current fiber"); 28 | std\assert(!std\currentFiber().isMain(), message: "Can know if fiber is main one"); 29 | 30 | for (i: int = 0; i < n; i = i + 1) { 31 | // error or yield is ignored if not called with a async call? 32 | _ = yield i; 33 | } 34 | 35 | return "Counting is done!"; 36 | } 37 | 38 | fun fail() > bool !> str { 39 | throw "This fiber failed"; 40 | 41 | return false; 42 | } 43 | 44 | fun caughFiberFail() > bool !> str { 45 | final fiber = &fail(); 46 | 47 | return resolve fiber; 48 | } 49 | 50 | test "Throw inside a fiber" { 51 | std\assert(caughFiberFail() catch true, message: "Caught an error from a fiber"); 52 | } 53 | 54 | fun closedUpvalue() > fun () > str { 55 | final upvalue = "joe"; 56 | 57 | return fun () > str => "hello {upvalue}"; 58 | } 59 | 60 | test "Opened upvalue in fiber" { 61 | final upvalue = "world"; 62 | 63 | final fiberFn = fun () > str => "hello {upvalue}"; 64 | std\assert(resolve &fiberFn() == "hello world", message: "Fiber could use an opened upvalue"); 65 | } 66 | 67 | test "Closed upvalue in fiber" { 68 | std\assert(resolve &closedUpvalue()() == "hello joe", message: "Fiber could use a closed upvalue"); 69 | } 70 | 71 | test "Wrapping call inside complex expressions" { 72 | final map = { 73 | "hello": fun () > str => "hello world", 74 | }; 75 | 76 | std\assert(resolve &map["hello"]?() == "hello world", message: "Could warp function call in a complex expression"); 77 | } 78 | -------------------------------------------------------------------------------- /tests/039-buffer.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | import "buffer" as _; 3 | 4 | test "Reading and writing in a buffer" { 5 | final buffer = Buffer.init(); 6 | 7 | buffer.writeInt("hello world".len()); 8 | buffer.write("hello world"); 9 | buffer.writeDouble(1238.324); 10 | buffer.writeBoolean(true); 11 | 12 | final expected = [ 13 | // "hello world".len() 14 | 11, 0, 0, 0, 0, 0, 15 | // "hello world" 16 | 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 17 | // 1238.324 18 | 158, 239, 167, 198, 75, 89, 147, 64, 19 | // true 20 | 1 ]; 21 | 22 | foreach (i, char in buffer.toString()) { 23 | std\assert(expected[i] == char.byte(0), message: "Buffer has expected content"); 24 | } 25 | 26 | std\assert(buffer.len() == expected.len(), message: "wrote expected number of bytes"); 27 | 28 | final len = buffer.readInt() ?? -1; 29 | std\assert(len == 11, message: "could read number"); 30 | 31 | final res = buffer.read(len); 32 | std\assert(res == "hello world", message: "could read n bytes got `{res}`"); 33 | std\assert(buffer.readDouble() == 1238.324, message: "could read double"); 34 | std\assert(buffer.readBoolean() == true, message: "could read boolean"); 35 | 36 | buffer.empty(); 37 | 38 | std\assert(buffer.len() == 0, message: "could empty buffer"); 39 | } 40 | -------------------------------------------------------------------------------- /tests/040-bitwise.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | test "Bitwise (constant folded)" { 4 | std\assert(15 << 3 == 120, message: "<<"); 5 | std\assert(15 >> 3 == 1, message: ">>"); 6 | std\assert(15 ^ 3 == 12, message: "^"); 7 | std\assert(15 | 3 == 15, message: "\\"); 8 | std\assert(~15 == -16, message: "~"); 9 | std\assert(12 & 23 == 4, message: "&"); 10 | } 11 | 12 | test "Bitwise" { 13 | final a = 15; 14 | final b = 12; 15 | 16 | std\assert(a << 3 == 120, message: "<<"); 17 | std\assert(a >> 3 == 1, message: ">>"); 18 | std\assert(a ^ 3 == 12, message: "^"); 19 | std\assert(a | 3 == a, message: "\\"); 20 | std\assert(~a == -16, message: "~"); 21 | std\assert(b & 23 == 4, message: "&"); 22 | } 23 | -------------------------------------------------------------------------------- /tests/041-iterator.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | fun fibonacci(n: int) > void *> int? { 4 | var n1 = 0; 5 | var n2 = 1; 6 | var next: int? = null; 7 | 8 | for (i: int = 0; i < n; i = i + 1) { 9 | _ = yield n1; 10 | next = n1 + n2; 11 | n1 = n2; 12 | n2 = next!; 13 | } 14 | } 15 | 16 | test "finobacci generator" { 17 | final suite = [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 ]; 18 | var i = 0; 19 | foreach (n in &fibonacci(10)) { 20 | std\assert(suite[i] == n, message: "could iterate over fiber"); 21 | 22 | i = i + 1; 23 | } 24 | } 25 | 26 | object Hello { 27 | fun getRange() > [int] *> int? { 28 | final list: mut [int] = mut []; 29 | foreach (i in 0..10) { 30 | _ = yield i; 31 | 32 | list.append(i); 33 | } 34 | 35 | return list; 36 | } 37 | } 38 | 39 | test "dot async call" { 40 | final hello = Hello{}; 41 | 42 | foreach (i in &hello.getRange()) { 43 | std\print("i {i}"); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tests/042-anonymous-objects.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | fun getInfo() > obj{ name: str, age: int } { 4 | return .{ 5 | name = "Joe", 6 | age = 36, 7 | }; 8 | } 9 | 10 | test "Anonymous objects" { 11 | final info = getInfo(); 12 | // Two anonymous type matches 13 | _: obj{ name: str, age: int } = info; 14 | 15 | std\assert(info.name == "Joe" and info.age == 36, message: "Could declare, instanciate and acces anonymous objects"); 16 | std\assert(info is obj{ name: str, age: int }, message: "Type safety works with anonymous object"); 17 | } 18 | 19 | test "Named variable init" { 20 | final name = "Joe"; 21 | final age = 42; 22 | 23 | final person = .{ name, age }; 24 | 25 | std\assert(person.name == "Joe" and person.age == 42); 26 | } 27 | 28 | fun getPayload::(data: T) > obj{ data: T } { 29 | return .{ 30 | data = data, 31 | }; 32 | } 33 | 34 | test "Anonymous object with generics" { 35 | final payload = getPayload::(42); 36 | 37 | std\assert(payload.data == 42, message: "Could use anonymous object with generic"); 38 | } 39 | 40 | fun callMe(o: obj{ x: int, y: int }) > void { 41 | std\assert(o.x == 12 and o.y == 13); 42 | } 43 | 44 | test "anonymous objects with different fields order" { 45 | callMe(.{ y = 13, x = 12 }); 46 | } 47 | 48 | // placeholders in anonymous object fields works 49 | object A { 50 | board: mut [int], 51 | 52 | fun free() > void { 53 | foreach (x, y in this.board) { 54 | var board: mut [obj{ x: int }] = mut []; 55 | 56 | board.append(.{ x = x }); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/044-break-continue.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | test "continue properly jumps and closes scope" { 4 | foreach (value in 1..5) { 5 | _ = "hello there!"; 6 | if (value == 3) { 7 | if (true) { 8 | continue; 9 | } 10 | } 11 | } 12 | 13 | final anotherRandomLocal = "bye there"; 14 | 15 | std\assert(anotherRandomLocal == "bye there", message: "continue properly closed revelant scopes"); 16 | } 17 | 18 | test "break properly jumps and closes scope" { 19 | foreach (value in 1..5) { 20 | _ = "hello there!"; 21 | if (value == 3) { 22 | if (true) { 23 | break; 24 | } 25 | } 26 | _ = "after break"; 27 | } 28 | 29 | final anotherRandomLocal = "bye there"; 30 | 31 | std\assert(anotherRandomLocal == "bye there", message: "break properly closed revelant scopes"); 32 | } 33 | -------------------------------------------------------------------------------- /tests/045-mutual-import.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | import "tests/utils/import-b"; 3 | import "tests/utils/import-a"; 4 | 5 | test "Mutual import" { 6 | std\print("t: {a\Hello}"); 7 | b\printClass(); 8 | std\print("{b\hello}"); 9 | std\assert(b\hello is a\Hello, message: "multiple import of the same script produce the same globals"); 10 | } 11 | -------------------------------------------------------------------------------- /tests/046-try-catch.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | fun willFail() > void !> str { 4 | throw "Yolo"; 5 | } 6 | 7 | fun partialCatch() > void !> str { 8 | try { 9 | willFail(); 10 | 11 | throw 12; 12 | } catch (_: int) { 13 | std\assert(false, message: "unreachable"); 14 | } 15 | } 16 | 17 | fun returnFromCatch() > int { 18 | try { 19 | willFail(); 20 | 21 | return 12; 22 | } catch (_: str) { 23 | return 21; 24 | } 25 | 26 | return 31; 27 | } 28 | 29 | test "Try catch" { 30 | var setme = ""; 31 | 32 | try { 33 | _ = "i'm local to this try block"; 34 | 35 | setme = "yes"; 36 | 37 | willFail(); 38 | 39 | partialCatch(); 40 | 41 | std\assert(false, message: "unreachable"); 42 | } catch (error: str) { 43 | _ = "a local in catch clause"; 44 | 45 | std\assert(error == "Yolo", message: "caught error"); 46 | } catch (_: int) { 47 | std\assert(false, message: "unreachable"); 48 | } catch { 49 | std\assert(false, message: "unreachable"); 50 | } 51 | 52 | final afterLocal = "bye"; 53 | 54 | std\assert(setme == "yes", message: "error was handled and code continues"); 55 | 56 | std\assert(returnFromCatch() == 21, message: "return from catch clause works"); 57 | 58 | std\assert(afterLocal == "bye", message: "catch closed its scope"); 59 | } 60 | 61 | test "catch any catches everything" { 62 | var caught = false; 63 | try { 64 | willFail(); 65 | } catch (error: any) { 66 | caught = true; 67 | std\assert(error is str, message: "Could cath any error"); 68 | } 69 | 70 | std\assert(caught, message: "Could catch any error"); 71 | } 72 | -------------------------------------------------------------------------------- /tests/047-if-arrow.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | test "If arrow" { 4 | final opt: int? = null; 5 | final optS: str? = "hello"; 6 | 7 | if (opt -> _) { 8 | std\assert(false, message: "unreachable"); 9 | } 10 | 11 | if (optS -> unwrapped) { 12 | std\assert(unwrapped == "hello", message: "Could if-arrow unwrap"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/048-generics.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | fun count::(list: [T]) > int { 4 | return list.len(); 5 | } 6 | 7 | test "Simple generic" { 8 | std\assert(count::([ 1, 2, 3 ]) == 3, message: "could use generics"); 9 | } 10 | 11 | fun extractList::(data: obj{ list: [T] }) > [T] { 12 | return data.list; 13 | } 14 | 15 | test "Generic within anonymous object" { 16 | final list = [ 1, 2, 3 ]; 17 | 18 | std\assert(extractList::(.{ list = list }) == list, message: "could use generic within anonymous object"); 19 | } 20 | 21 | fun countMap::(map: {K: V}) > int { 22 | return map.size(); 23 | } 24 | 25 | test "Multiple generic types" { 26 | final map = { 27 | "one": 1, 28 | "two": 2, 29 | "three": 3, 30 | }; 31 | 32 | std\assert(countMap::(map) == 3, message: "could use multiple generic types"); 33 | } 34 | 35 | fun extractMap::(data: obj{ map: {K: V} }) > {K: V} { 36 | return data.map; 37 | } 38 | 39 | test "Generic within anonymous object and map" { 40 | final map = { 41 | "one": 1, 42 | "two": 2, 43 | "three": 3, 44 | }; 45 | 46 | std\assert(extractMap::(.{ map = map }) == map, message: "could use generic within anonymous object"); 47 | } 48 | 49 | fun countShuffleGenerics::() > fun (a: [A], b: [B]) > int { 50 | return fun (a: [A], b: [B]) > int => a.len() + b.len(); 51 | } 52 | 53 | test "Generic in lambda function definition" { 54 | final a = [ "one", "two", "three" ]; 55 | final b = [ 1, 2, 3 ]; 56 | 57 | std\assert(countShuffleGenerics::()(a, b: b) == 6, message: "could use generic in lambda function definition"); 58 | } 59 | 60 | fun genericReturn::(value: T) > T { 61 | return value; 62 | } 63 | 64 | test "generic return" { 65 | std\assert(genericReturn::(12) == 12, message: "could use return of generic type"); 66 | } 67 | 68 | fun fiber::(data: [T]) > void *> T? { 69 | foreach (element in data) { 70 | _ = yield element; 71 | } 72 | } 73 | 74 | test "Generic with fibers" { 75 | final f = &fiber::([ 1, 2, 3 ]); 76 | 77 | var sum = 0; 78 | while (!f.over()) { 79 | sum = sum + resume f ?? 0; 80 | } 81 | 82 | std\assert(sum == 6, message: "could use generic in fiber definition"); 83 | } 84 | 85 | test "Generics in placeholders" { 86 | std\assert( 87 | countAgain::([ 1, 2, 3 ]) == 3, 88 | message: "Could use generics with placeholders", 89 | ); 90 | } 91 | 92 | fun countAgain::(list: [T]) > int { 93 | return list.len(); 94 | } 95 | 96 | fun doubleGeneric::(lambda: fun::() > int) > int { 97 | return lambda::(); 98 | } 99 | 100 | test "Generic with generic lambda parameter" { 101 | std\assert( 102 | doubleGeneric::( 103 | fun::() > int => 12, 104 | ) == 12, 105 | message: "could defined multiple generics", 106 | ); 107 | } 108 | -------------------------------------------------------------------------------- /tests/049-multiline-strings.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | import "serialize"; 3 | 4 | test "Multiline strings" { 5 | final multi = `\{ 6 | "some": "json", 7 | "yes": {3 + 12}, 8 | {` 9 | "another": "one" 10 | `} 11 | }`; 12 | 13 | final json = serialize\jsonDecode(multi); 14 | 15 | std\assert(json.q([ "yes" ]).integer() == 15, message: "multiline string is valid"); 16 | std\assert(json.q([ "another" ]).string() == "one", message: "multiline string is valid"); 17 | } 18 | -------------------------------------------------------------------------------- /tests/050-protocols.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | protocol Comparable { 4 | fun greater(than: Comparable) > bool; 5 | } 6 | 7 | protocol Nameable { 8 | mut fun rename(name: str) > void; 9 | } 10 | 11 | object Person { 12 | name: str, 13 | age: int, 14 | 15 | fun greater(than: Comparable) > bool !> str { 16 | if (than as other: Person) { 17 | return this.age > other.age; 18 | } 19 | 20 | throw "Can't compare to {than}"; 21 | } 22 | 23 | mut fun rename(name: str) > void { 24 | this.name = name; 25 | } 26 | } 27 | 28 | /// A friendly pet 29 | object Pet { 30 | name: str, 31 | 32 | mut fun rename(name: str) > void { 33 | this.name = name; 34 | } 35 | } 36 | 37 | test "Protocols" { 38 | final joe = mut Person{ 39 | name = "Joe", 40 | age = 25, 41 | }; 42 | 43 | final bob = Person{ 44 | name = "Bob", 45 | age = 56, 46 | }; 47 | 48 | final bandit = mut Pet{ 49 | name = "bandit", 50 | }; 51 | 52 | std\assert(bob.greater(joe), message: "could implement protocol"); 53 | 54 | var nameable: Nameable = bandit; 55 | nameable = joe; 56 | 57 | final nameables: [mut Nameable] = [ bandit, joe ]; 58 | final newNames = [ "Chili", "Nick" ]; 59 | foreach (i, item in nameables) { 60 | item.rename(newNames[i]); 61 | } 62 | 63 | std\assert(bandit.name == "Chili", message: "could call protocol method"); 64 | std\assert(joe.name == "Nick", message: "could call protocol method"); 65 | 66 | final map: {Nameable: bool} = { 67 | bandit: true, 68 | joe: false, 69 | }; 70 | 71 | std\assert(map[joe] == false, message: "could hold protocol as map key"); 72 | 73 | final mapValue: {str: Nameable} = { 74 | "bandit": bandit, 75 | "joe": joe, 76 | }; 77 | 78 | std\assert(mapValue["joe"] == joe, message: "could hold protocol as map value"); 79 | } 80 | -------------------------------------------------------------------------------- /tests/051-functional.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | test "list.forEach" { 4 | final data = [ 1, 2, 3, 4 ]; 5 | 6 | var sum = 0; 7 | data.forEach( 8 | fun (_: int, element: int) > void { 9 | sum = sum + element; 10 | }, 11 | ); 12 | 13 | std\assert(sum == 10, message: "could use list.forEach"); 14 | } 15 | 16 | test "list.map" { 17 | final data = [ 1, 2, 3, 4 ]; 18 | 19 | final mapped = data.map::(fun (_: int, element: int) => "{element}"); 20 | 21 | std\assert(mapped.join(", ") == "1, 2, 3, 4", message: "could use map"); 22 | } 23 | 24 | test "list.filter" { 25 | final data = [ 1, 2, 3, 4 ]; 26 | 27 | final odd = data.filter(fun (_: int, element: int) => element % 2 != 0); 28 | 29 | std\assert(odd.join(", ") == "1, 3", message: "could use filter"); 30 | } 31 | 32 | test "list.reduce" { 33 | final data = [ 1, 2, 3, 4 ]; 34 | 35 | final sum = data.reduce::( 36 | fun (_: int, element: int, accumulator: int) => accumulator + element, 37 | initial: 0, 38 | ); 39 | 40 | std\assert(sum == 10, message: "could use reduce"); 41 | } 42 | 43 | test "list.sort" { 44 | final data = mut [ 10, 3, 12, 0, 1, -3 ]; 45 | 46 | _ = data.sort(fun (left: int, right: int) => left < right); 47 | 48 | foreach (i, value in [ -3, 0, 1, 3, 10, 12 ]) { 49 | std\assert(data[i] == value, message: "list is not ordered"); 50 | } 51 | } 52 | 53 | test "map.forEach" { 54 | final map = { 55 | "five": 5, 56 | "two": 2, 57 | "one": 1, 58 | "three": 3, 59 | }; 60 | 61 | var sum = 0; 62 | map.forEach( 63 | fun (_: str, value: int) > void => sum = sum + value, 64 | ); 65 | 66 | std\assert(sum == 11, message: "could use map.forEach"); 67 | } 68 | 69 | test "map.map" { 70 | final map = { 71 | "five": 5, 72 | "two": 2, 73 | "one": 1, 74 | "three": 3, 75 | }; 76 | 77 | final inverted = map.map::( 78 | fun (key: str, value: int) => .{ key = value, value = key }, 79 | ); 80 | 81 | foreach (key, value in map) { 82 | std\assert(inverted[value] == key, message: "Could use map.map"); 83 | } 84 | } 85 | 86 | test "map.filter" { 87 | final map = { 88 | "five": 5, 89 | "two": 2, 90 | "one": 1, 91 | "three": 3, 92 | }; 93 | 94 | final filtered = map.filter( 95 | fun (_: str, value: int) => value % 2 != 0, 96 | ); 97 | 98 | std\assert(filtered["two"] == null, message: "Could use map.filter"); 99 | } 100 | 101 | test "map.reduce" { 102 | final map = { 103 | "five": 5, 104 | "two": 2, 105 | "one": 1, 106 | "three": 3, 107 | }; 108 | 109 | final sum = map.reduce::( 110 | fun (_: str, value: int, accumulator: int) => accumulator + value, 111 | initial: 0, 112 | ); 113 | 114 | std\assert(sum == 11, message: "could use reduce"); 115 | } 116 | 117 | test "map.sort" { 118 | final map = mut { 119 | "five": 5, 120 | "two": 2, 121 | "one": 1, 122 | "three": 3, 123 | }; 124 | 125 | _ = map.sort( 126 | fun (lhs: str, rhs: str) => map[lhs]! < map[rhs]!, 127 | ); 128 | 129 | final ordered = [ "one", "two", "three", "five" ]; 130 | var i = 0; 131 | foreach (key in map.keys()) { 132 | std\assert(ordered[i] == key, message: "Could sort map"); 133 | 134 | i = i + 1; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /tests/052-inline-if.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | test "ternary" { 4 | var value = if (true) 12 else 0; 5 | 6 | std\assert(value == 12, message: "could use constant inline if"); 7 | 8 | value = if ("hello".len() == 2) 0 else 12; 9 | 10 | std\assert(value == 12, message: "could use inline if"); 11 | } 12 | 13 | test "multiple branches" { 14 | final value = 12; 15 | 16 | final expr = if (value == 14) 17 | "hello" 18 | else if (value == 12) 19 | "yolo" 20 | else 21 | null; 22 | 23 | std\assert(expr == "yolo", message: "Could use multiple branches with inline if"); 24 | } 25 | 26 | test "inline if in expression" { 27 | final value = 12; 28 | 29 | std\assert( 30 | (if (value == 14) 31 | "hello" 32 | else if (value == 12) 33 | "yolo" 34 | else 35 | null) == "yolo", 36 | message: "Could use multiple branches inline if in expression" 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /tests/053-range.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | test "Range" { 4 | final limit = 10; 5 | final range = 0..limit; 6 | 7 | std\assert(range == 0..10, message: "Could compare ranges"); 8 | std\assert(range.low() == 0, message: "Could get low limit of range"); 9 | std\assert(range.high() == 10, message: "Could get high limit of range"); 10 | 11 | final list = range.toList(); 12 | std\assert(list.len() == 10, message: "Could create list from range"); 13 | 14 | var sum = 0; 15 | foreach (n in 0..10) { 16 | sum = sum + n; 17 | } 18 | std\assert(sum == 45, message: "Could iterate over range"); 19 | 20 | std\assert(range.len() == 10, message: "Could get range length"); 21 | } 22 | 23 | test "Inverted range" { 24 | final limit = 0; 25 | final range = 10..limit; 26 | 27 | std\assert((0..10).invert() == range, message: "Could invert range"); 28 | std\assert(range.low() == 10, message: "Could get low limit of range"); 29 | std\assert(range.high() == 0, message: "Could get high limit of range"); 30 | 31 | final list = range.toList(); 32 | std\assert(list.len() == 10, message: "Could create list from inverted range"); 33 | 34 | var sum = 0; 35 | foreach (n in 10..0) { 36 | sum = sum + n; 37 | } 38 | std\assert(sum == 55, message: "Could iterate over inverted range"); 39 | 40 | std\assert(range.len() == 10, message: "Could get range length"); 41 | } 42 | -------------------------------------------------------------------------------- /tests/056-crypto.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | import "crypto" as _; 3 | 4 | test "hash" { 5 | std\assert( 6 | "c3fcd3d76192e4007dfb496cca67e13b" == hash(HashAlgorithm.Md5, data: "abcdefghijklmnopqrstuvwxyz").hex(), 7 | message: "md5", 8 | ); 9 | 10 | std\assert( 11 | "a9993e364706816aba3e25717850c26c9cd0d89d" == hash(HashAlgorithm.Sha1, data: "abc").hex(), 12 | message: "sha1", 13 | ); 14 | 15 | std\assert( 16 | "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" == hash(HashAlgorithm.Sha256, data: "abc").hex(), 17 | message: "sha256", 18 | ); 19 | 20 | std\assert( 21 | "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532" == hash(HashAlgorithm.Sha3256, data: "abc").hex(), 22 | message: "sha3-256", 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /tests/057-any.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | test "any" { 4 | final anything: any = "hello"; 5 | 6 | std\assert(anything is str, message: "can manipulate any typed value"); 7 | 8 | if (anything as astring: str) { 9 | std\assert(astring.len() == "hello".len(), message: "could cast any"); 10 | } 11 | } 12 | 13 | test "any placeholder" { 14 | std\assert(placeholder is str, message: "can manipulate placeholder with any typed value"); 15 | 16 | if (placeholder as astring: str) { 17 | std\assert(astring.len() == "hello".len(), message: "could cast placeholder any"); 18 | } 19 | } 20 | 21 | final placeholder: any = "hello"; 22 | 23 | test "as?" { 24 | final anything: any = 12; 25 | 26 | std\assert((anything as? int) == 12, message: "as?"); 27 | 28 | std\assert((anything as? str) == null, message: "as?"); 29 | } 30 | 31 | test "list of any" { 32 | final list = [ 1, true, 12.4, "hello" ]; 33 | 34 | foreach (element in list) { 35 | std\print("{element}"); 36 | } 37 | } 38 | 39 | test "map of any" { 40 | final map: {str: any} = { 41 | "hello": true, 42 | "world": "one", 43 | }; 44 | 45 | foreach (key, element in map) { 46 | std\print("{key}: {element}"); 47 | } 48 | 49 | final map2: {any: str} = { 50 | "hello": "true", 51 | true: "one", 52 | }; 53 | 54 | foreach (key, element in map2) { 55 | std\print("{key}: {element}"); 56 | } 57 | 58 | final map3: {any: any} = { 59 | "hello": 1, 60 | true: "one", 61 | }; 62 | 63 | foreach (key, element in map3) { 64 | std\print("{key}: {element}"); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tests/058-ffi.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | import "buffer" as _; 3 | import "ffi"; 4 | 5 | zdef( 6 | "tests/utils/libforeign", 7 | ` 8 | fn acos(value: f64) f64; 9 | fn fprint(msg: [*:0]const u8) void; 10 | fn sum(values: [*]i32, len: i32) i32; 11 | ` 12 | ); 13 | 14 | test "scalar type" { 15 | std\assert(acos(0.12) == 1.4505064444001086, message: "Could call FFI function with scalar arguments"); 16 | } 17 | 18 | test "cstring" { 19 | fprint(ffi\cstr("hello world")); 20 | } 21 | 22 | test "pointer with Buffer" { 23 | final buffer = Buffer.init(); 24 | 25 | buffer.writeZ::("i32", values: [ 1, 2, 3 ]); 26 | buffer.writeZ::("i32", values: [ 1, 2, 3 ]); 27 | 28 | try { 29 | // Arguably, the zig type parameter could a constant and be built at compile time 30 | // But: that would require a new type 31 | // Since we cache the result of the type parsing this is roughly equivalent 32 | buffer.writeZ::("u64", values: [ 1 ]); 33 | std\assert(false, message: "Using bad buzz type triggers error"); 34 | } catch (_: ffi\FFITypeMismatchError) { 35 | std\assert(true, message: "Using bad buzz type triggers error"); 36 | } 37 | 38 | final i32align = ffi\alignOf("i32"); 39 | final len = buffer.len(align: i32align); 40 | std\assert(len == buffer.len() / 4, message: "Len align"); 41 | final total = sum(buffer.ptr(), len: len); 42 | 43 | std\assert(total == 12, message: "Could call FFI function with pointer argument"); 44 | 45 | var readTotal = 0; 46 | foreach (i in 0..buffer.len(align: i32align)) { 47 | readTotal = readTotal + buffer.readZAt::( 48 | at: i, 49 | zigType: "i32", 50 | ); 51 | } 52 | 53 | std\assert(readTotal == total, message: "Could read from pointer"); 54 | 55 | final subTotal = sum(buffer.ptr(1, align: i32align), len: len - 1); 56 | 57 | std\assert(subTotal == 11, message: "Could get ptr at offset"); 58 | } 59 | 60 | zdef( 61 | "tests/utils/libforeign", 62 | ` 63 | const Data = extern struct { 64 | id: i32, 65 | msg: [*:0]const u8, 66 | value: f64, 67 | }; 68 | 69 | fn get_data_msg(data: *Data) [*:0]const u8; 70 | fn set_data_id(data: *Data) void; 71 | ` 72 | ); 73 | 74 | test "struct" { 75 | final data = Data{ 76 | msg = ffi\cstr("bye world"), 77 | id = 123, 78 | value = 42.0, 79 | }; 80 | 81 | std\assert(data.msg == "bye world\0", message: "Could instanciate Zig struct"); 82 | std\assert(data.id == 123, message: "Could instanciate Zig struct"); 83 | 84 | data.id = 42; 85 | 86 | std\assert(data.id == 42, message: "Could set Zig struct field"); 87 | 88 | std\assert(get_data_msg(data) == "bye world\0", message: "Could use foreign function with struct ptr param"); 89 | 90 | set_data_id(data); 91 | 92 | std\assert(data.id == 84, message: "Could use foreign function with struct ptr param that modifies the struct"); 93 | } 94 | 95 | test "write/read struct in buffer" { 96 | final data = Data{ 97 | msg = ffi\cstr("bye world"), 98 | id = 123, 99 | value = 42.0, 100 | }; 101 | 102 | final buffer = Buffer.init(); 103 | 104 | buffer.writeStruct::(Data, values: [ data ]); 105 | 106 | std\assert(buffer.toString().len() == ffi\sizeOfStruct(Data), message: "Could write struct to buffer"); 107 | 108 | final read = buffer.readStruct::(Data); 109 | 110 | std\assert( 111 | read.msg == data.msg and read.id == data.id, 112 | message: "Could read struct from buffer", 113 | ); 114 | } 115 | 116 | zdef( 117 | "tests/utils/libforeign", 118 | ` 119 | pub const Flag = extern struct { 120 | id: i32, 121 | value: bool, 122 | }; 123 | 124 | pub const Misc = extern union { 125 | id: i32, 126 | data: Data, 127 | flag: Flag, 128 | }; 129 | 130 | fn get_misc_msg(misc: *Misc) [*:0]const u8; 131 | fn get_misc_flag(misc: *Misc) bool; 132 | fn set_misc_id(misc: *Misc, new_id: i32) void; 133 | ` 134 | ); 135 | 136 | // test "union" { 137 | // final misc = Misc{ 138 | // data = Data{ 139 | // id = 123, 140 | // msg = ffi\cstr("hello world"), 141 | // value = 42.0, 142 | // } 143 | // }; 144 | 145 | // std\print(ffi\rawData(misc).hex()); 146 | // std\print("value = {misc.data.value}"); 147 | 148 | // std\assert(get_misc_msg(misc) == ffi\cstr("hello world"), message: "Could read union field"); 149 | 150 | // misc.flag = Flag{ 151 | // id = 123, 152 | // value = true, 153 | // }; 154 | 155 | // std\print(ffi\rawData(misc).hex()); 156 | 157 | // std\assert(get_misc_flag(misc), message: "Could read union field"); 158 | 159 | // misc.id = 321; 160 | 161 | // std\print(ffi\rawData(misc).hex()); 162 | 163 | // std\assert(misc.id == 321, message: "Got expected memory layout of a C union"); 164 | // std\assert(misc.data.id == 321, message: "Got expected memory layout of a C union"); 165 | // std\assert(misc.flag.id == 321, message: "Got expected memory layout of a C union"); 166 | // } 167 | -------------------------------------------------------------------------------- /tests/059-types-as-value.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | import "debug"; 3 | 4 | object A {} 5 | 6 | enum B { 7 | case, 8 | } 9 | 10 | fun dumpType(myType: type) > void { 11 | std\print("{myType}"); 12 | } 13 | 14 | protocol C {} 15 | 16 | test "Types as value" { 17 | _ = ; 18 | final another = ; 19 | final again = ; 20 | 21 | std\assert(another == again, message: "Can compare type values"); 22 | } 23 | 24 | test "typeof" { 25 | std\assert(typeof A{} == , message: "typeof operator instance"); 26 | std\assert(typeof B.case == , message: "typeof operator case"); 27 | std\assert(typeof "hello" == , message: "typeof operator str"); 28 | std\assert(typeof true == , message: "typeof operator bool"); 29 | std\assert(typeof null == , message: "typeof operator null"); 30 | std\assert(typeof 1 == , message: "typeof operator int"); 31 | std\assert(typeof 3.14 == , message: "typeof operator double"); 32 | std\assert(typeof $"hello" == , message: "typeof operator pattern"); 33 | std\assert(typeof dumpType == void *> void>, message: "typeof operator"); 34 | } 35 | 36 | test "type argument" { 37 | dumpType(typeof A); 38 | } 39 | 40 | zdef( 41 | "tests/utils/libforeign", 42 | ` 43 | const Data = extern struct { 44 | msg: [*:0]const u8, 45 | id: i32, 46 | }; 47 | ` 48 | ); 49 | 50 | test "necessary weirdness" { 51 | std\assert(() == typeof A{}, message: "typeof Object == typeof Object instance"); 52 | 53 | std\assert(typeof C == , message: "protocol is a type at runtime"); 54 | std\assert(typeof Data == , message: "fstruct is a type at runtime"); 55 | } 56 | 57 | fun generic::(value: T) > T { 58 | return value; 59 | } 60 | 61 | fun typeArg(T: type, value: str) > str { 62 | debug\dump(T); 63 | return value; 64 | } 65 | 66 | test "generic type expression ambiguity" { 67 | debug\dump(generic::("hello")); 68 | debug\dump(typeArg(, value: "hello")); 69 | } 70 | -------------------------------------------------------------------------------- /tests/060-free-identifiers.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | test "Free identifiers" { 4 | final @"non-standard-identifier" = "hello world"; 5 | 6 | std\assert(@"non-standard-identifier" == "hello world", message: "Could use non-standard identifiers"); 7 | } 8 | 9 | object A { 10 | @"type": str, 11 | } 12 | 13 | test "Free identifier as object field" { 14 | final a = A{ 15 | @"type" = "Hello", 16 | }; 17 | 18 | std\assert(a.@"type" == "Hello", message: "Could use non-standard identifiers as object field"); 19 | } 20 | -------------------------------------------------------------------------------- /tests/061-utf8.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | test "utf8" { 4 | final msg = "hello 🔥 buzz !"; 5 | 6 | std\assert(msg.utf8Len() == 14, message: "Could get length of utf8 string"); 7 | std\assert(msg.utf8Valid(), message: "Could validate utf8 string"); 8 | 9 | final invalid = "hello \232 world!"; 10 | 11 | std\assert(!invalid.utf8Valid(), message: "Could not validate invalid utf8 string"); 12 | 13 | final codepoints = "I'm 🦇-man so 🔥 !".utf8Codepoints(); 14 | std\assert(codepoints[4] == "🦇", message: "Could get utf8 string codepoints"); 15 | } 16 | -------------------------------------------------------------------------------- /tests/062-discarded-value.buzz: -------------------------------------------------------------------------------- 1 | _ = "unused"; 2 | 3 | test "discarding value" { 4 | _ = "i'm discarded"; 5 | } 6 | -------------------------------------------------------------------------------- /tests/063-nullable-default.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | object Person { 4 | name: str, 5 | age: int?, 6 | } 7 | 8 | test "Nullable object field has a default value at null" { 9 | final person = Person{ 10 | name = "Joe", 11 | }; 12 | 13 | std\assert(person.age == null, message: "Nullable object field has a default value at null"); 14 | } 15 | 16 | test "Nullable variable has a default value at null" { 17 | final hello: int?; 18 | 19 | std\assert(hello == null, message: "Nullable variable has default value at null"); 20 | } 21 | -------------------------------------------------------------------------------- /tests/064-throw-inside-try.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | fun willFail() > void !> str { 4 | if (false) { 5 | throw "Hey"; 6 | } 7 | } 8 | 9 | object SomeError {} 10 | 11 | fun run() > void { 12 | try { 13 | willFail(); 14 | 15 | throw SomeError{}; 16 | } catch (_: str) { 17 | std\assert(false, message: "Could throw inside try/catch"); 18 | } catch (_: SomeError) { 19 | std\assert(true, message: "Could throw inside try/catch"); 20 | } 21 | } 22 | 23 | test "Throw inside try/catch" { 24 | run(); 25 | } 26 | -------------------------------------------------------------------------------- /tests/065-inferred-var-type.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | object Person { 4 | name: str = "Joe", 5 | } 6 | 7 | final integer = 42; 8 | final float = 42.42; 9 | 10 | test "Inferring variable declaration type" { 11 | std\assert(integer is int, message: "Could infer global variable declaration type"); 12 | std\assert(float is double, message: "Could infer global constant declaration type"); 13 | 14 | final string = "hello"; 15 | 16 | std\assert(string is str, message: "Could infer variable declaration type"); 17 | 18 | final person = Person{}; 19 | 20 | std\assert(person is Person, message: "Could infer constant declaration type"); 21 | } 22 | -------------------------------------------------------------------------------- /tests/066-object-generics.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | object Payload:: { 4 | data: mut {K: V}, 5 | } 6 | 7 | test "Objects generics" { 8 | final payload = Payload::{ 9 | data = mut { "one": 1 }, 10 | }; 11 | 12 | payload.data["two"] = 2; 13 | 14 | std\assert(payload is Payload::, message: "Could use objects generics"); 15 | std\assert(payload.data["two"] == 2, message: "Could use objects generics"); 16 | } 17 | -------------------------------------------------------------------------------- /tests/068-testing.buzz: -------------------------------------------------------------------------------- 1 | import "testing" as _; 2 | 3 | test "Test std lib" { 4 | final t = Tester.init( 5 | beforeAll: fun (t: Tester) { 6 | t.assert(true); 7 | }, 8 | afterAll: fun (t: Tester) { 9 | t.assert(true); 10 | }, 11 | beforeEach: fun (t: Tester) { 12 | t.assert(true); 13 | }, 14 | afterEach: fun (t: Tester) { 15 | t.assert(true); 16 | }, 17 | ); 18 | 19 | t.it( 20 | "Should be an integer equal to 12", 21 | fn: fun () { 22 | final value = 12; 23 | 24 | t.assertOfType::(value); 25 | 26 | t.assertEqual::(value, expected: 12, message: "Yeah!"); 27 | }, 28 | ); 29 | 30 | t.it( 31 | "Should compare list elements", 32 | fn: fun () { 33 | t.assertAreNotEqual::([ 1, 2, 3 ], message: "Testing failure"); 34 | }, 35 | ); 36 | 37 | t.it( 38 | "Should compare list elements", 39 | fn: fun () { 40 | t.assertAreEqual::([ 1, 1, 1 ], message: "List of 1s"); 41 | }, 42 | ); 43 | 44 | t.it( 45 | "Should throw", 46 | fn: fun () { 47 | t.assertThrows::( 48 | fun () !> str { 49 | throw "Failing!"; 50 | }, 51 | message: "Should fail with str error", 52 | ); 53 | }, 54 | ); 55 | 56 | t.it( 57 | "Should not throw", 58 | fn: fun () { 59 | t.assertDoesNotThrow::( 60 | fun () {}, 61 | message: "Should fail with str error", 62 | ); 63 | }, 64 | ); 65 | 66 | t.summary(); 67 | } 68 | -------------------------------------------------------------------------------- /tests/069-named-expr.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | object Person { 4 | name: str, 5 | age: int, 6 | sex: bool, 7 | } 8 | 9 | test "Named expr object properties" { 10 | final name = "joe"; 11 | final age = 24; 12 | 13 | final person = Person{ 14 | name, 15 | age, 16 | sex = true, 17 | }; 18 | 19 | std\assert( 20 | person.name == "joe" and person.age == 24 and person.sex, 21 | message: "Could use named variable as object property value", 22 | ); 23 | } 24 | 25 | fun hello(name: str, age: int, sex: bool) > Person { 26 | return Person{ 27 | name, 28 | age, 29 | sex, 30 | }; 31 | } 32 | 33 | test "Name expr function argument" { 34 | final name = "joe"; 35 | final age = 24; 36 | 37 | final person = hello(name, age, sex: true); 38 | 39 | std\assert( 40 | person.name == "joe" and person.age == 24 and person.sex, 41 | message: "Could use named variable as function arguments", 42 | ); 43 | } 44 | -------------------------------------------------------------------------------- /tests/070-block-expression.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | test "block expression" { 4 | final value = from { 5 | std\print("doing stuff in my block..."); 6 | out "my value"; 7 | }; 8 | 9 | std\assert(value == "my value", message: "Could use block expression"); 10 | std\assert(value is str, message: "Could infer block expression type properly"); 11 | } 12 | -------------------------------------------------------------------------------- /tests/071-tail-call.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | fun mul(a: int, b: int) > int { 4 | return a * b; 5 | } 6 | 7 | fun tail(a: int, b: int) > int { 8 | return mul(a, b); 9 | } 10 | 11 | test "simple tail call" { 12 | std\assert(tail(a: 5, b: 2) == 10); 13 | } 14 | 15 | fun recursive(limit: int, current: int) > int { 16 | if (current > limit) { 17 | return current; 18 | } 19 | 20 | return recursive(limit, current: current + 1); 21 | } 22 | 23 | test "recursive tail call" { 24 | std\assert(recursive(limit: 5, current: 0) == 6); 25 | } 26 | 27 | object Tail { 28 | a: int, 29 | b: int, 30 | 31 | fun mul(c: int) > int { 32 | return (this.a + this.b) * c; 33 | } 34 | 35 | fun tail(c: int) > int { 36 | return this.mul(c); 37 | } 38 | } 39 | 40 | test "dot tail call" { 41 | final result = Tail{ 42 | a = 5, 43 | b = 2, 44 | }.tail(3); 45 | 46 | std\print("{result}"); 47 | } 48 | -------------------------------------------------------------------------------- /tests/072-labels.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | test "labeled break" { 4 | var i = 0; 5 | while (i < 100) :here { 6 | i = i + 1; 7 | 8 | if (i == 10) { 9 | break here; 10 | } 11 | } 12 | 13 | std\assert(i == 10); 14 | } 15 | 16 | test "labeled break in nested loop" { 17 | var i = 0; 18 | foreach (_ in 0..100) :here { 19 | _ = "hello"; 20 | 21 | while (i < 100) { 22 | i = i + 1; 23 | 24 | if (i == 10) { 25 | break here; 26 | } 27 | } 28 | } 29 | 30 | std\assert(i == 10); 31 | } 32 | 33 | test "labeled break in deeply nested loop" { 34 | var i = 0; 35 | foreach (j in 0..100) :here { 36 | _ = "hello"; 37 | 38 | while (j < 100) { 39 | _ = "bye"; 40 | 41 | while (i < 100) { 42 | i = i + 1; 43 | 44 | if (i == 10) { 45 | break here; 46 | } 47 | } 48 | } 49 | } 50 | 51 | std\assert(i == 10); 52 | } 53 | 54 | test "labeled continue" { 55 | var i = 0; 56 | foreach (j in 0..10) :here { 57 | if (j == 3) { 58 | continue here; 59 | } 60 | 61 | i = i + j; 62 | } 63 | 64 | std\assert(i == 42); 65 | } 66 | -------------------------------------------------------------------------------- /tests/073-tuples.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | fun pack(names: [str]) > obj{ :str, :str, :str } { 4 | return .{ 5 | names[0], 6 | names[1], 7 | names[2], 8 | }; 9 | } 10 | 11 | test "Tuples" { 12 | final tuple = .{ "joe", "doe" }; 13 | 14 | std\assert(tuple.@"0" == "joe" and tuple.@"1" == "doe"); 15 | 16 | final name = "Joe"; 17 | final age = 42; 18 | 19 | // Forced tuple 20 | final another = .{ (name), (age) }; 21 | 22 | std\assert(another.@"0" == "Joe" and another.@"1" == 42); 23 | 24 | final names = pack([ "Joe", "John", "James" ]); 25 | 26 | std\assert(names.@"0" == "Joe" and names.@"1" == "John"); 27 | } 28 | -------------------------------------------------------------------------------- /tests/074-checked-subscript.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | test "List checked subscript" { 4 | final list = [ 1, 2, 3 ]; 5 | std\assert(list[?10] is int?); 6 | std\assert(list[?10] == null); 7 | std\assert(list[?0] == 1); 8 | } 9 | 10 | test "String checked subscript" { 11 | std\assert("hello"[?10] is str?); 12 | std\assert("hello"[?10] == null); 13 | std\assert("hello"[?0] == "h"); 14 | } 15 | -------------------------------------------------------------------------------- /tests/bench/001-btree.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | import "math"; 3 | 4 | object Node { 5 | left: Node? = null, 6 | right: Node? = null, 7 | } 8 | 9 | fun bottomUpTree(depth: int) > Node { 10 | if (depth > 0) { 11 | return Node{ 12 | left = bottomUpTree(depth - 1), 13 | right = bottomUpTree(depth - 1), 14 | }; 15 | } 16 | 17 | return Node{}; 18 | } 19 | 20 | fun itemCheck(tree: Node) > int { 21 | if (tree.left -> left) { 22 | return 1 + itemCheck(left) + itemCheck(tree.right!); 23 | } else { 24 | return 1; 25 | } 26 | 27 | return 0; 28 | } 29 | 30 | fun btree(N: int) > void { 31 | final mindepth = 4; 32 | var maxdepth = mindepth + 2; 33 | if (maxdepth < N) { 34 | maxdepth = N; 35 | } 36 | 37 | final stretchdepth = maxdepth + 1; 38 | final stretchtree = bottomUpTree(stretchdepth); 39 | std\print("stretch tree of depth {stretchdepth},\t check: {itemCheck(stretchtree)}"); 40 | 41 | final longlivedtree = bottomUpTree(maxdepth); 42 | for (depth: int = mindepth; depth < maxdepth; depth = depth + 2) { 43 | final iterations = std\toInt(math\pow(x: 2.0, y: std\toDouble(maxdepth - depth + mindepth)) catch 0.0); 44 | var check = 0; 45 | foreach (_ in 0..iterations) { 46 | check = check + itemCheck(bottomUpTree(depth)); 47 | } 48 | 49 | std\print("{iterations}\t trees of depth {depth}\t check: {check}"); 50 | } 51 | 52 | std\print("long lived tree of depth {maxdepth}\t check: {itemCheck(longlivedtree)}"); 53 | } 54 | 55 | fun main(args: [str]) > void !> any { 56 | btree( 57 | std\parseInt(args[?0] ?? "3") ?? 3 58 | ); 59 | } 60 | -------------------------------------------------------------------------------- /tests/bench/002-merkle.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | import "math"; 3 | import "errors"; 4 | 5 | object Node { 6 | left: mut Node? = null, 7 | right: mut Node? = null, 8 | value: int? = null, 9 | hash: int? = null, 10 | } 11 | 12 | fun makeTree(depth: int) > mut Node { 13 | if (depth > 0) { 14 | return mut Node{ 15 | left = makeTree(depth - 1), 16 | right = makeTree(depth - 1), 17 | }; 18 | } 19 | 20 | return mut Node{ 21 | value = 1 22 | }; 23 | } 24 | 25 | fun check(tree: Node) > bool { 26 | if (tree.hash != null) { 27 | if (tree.value != null) { 28 | return true; 29 | } else { 30 | return check(tree.left!) and check(tree.right!); 31 | } 32 | } 33 | 34 | return false; 35 | } 36 | 37 | fun calHash(tree: mut Node) > void { 38 | if (tree.hash == null) { 39 | if (tree.value -> value) { 40 | tree.hash = value; 41 | } else { 42 | calHash(tree.left!); 43 | calHash(tree.right!); 44 | 45 | tree.hash = tree.left?.hash ?? 0 + tree.right?.hash ?? 0; 46 | } 47 | } 48 | } 49 | 50 | fun main(args: [str]) > void !> any { 51 | final N = std\parseInt(args[?0] ?? "6") ?? 6; 52 | 53 | final mindepth = 4; 54 | var maxdepth = mindepth + 2; 55 | if (maxdepth < N) { 56 | maxdepth = N; 57 | } 58 | 59 | final stretchdepth = maxdepth + 1; 60 | final stretchtree = makeTree(stretchdepth); 61 | calHash(stretchtree); 62 | 63 | std\print("stretch tree of depth {stretchdepth}\t root hash: {stretchtree.hash ?? 0} check: {check(stretchtree)}"); 64 | 65 | final longlivedtree = makeTree(maxdepth); 66 | for (depth: int = mindepth; depth < maxdepth; depth = depth + 2) { 67 | final iterations = std\toInt( 68 | math\pow( 69 | x: 2.0, 70 | y: std\toDouble(maxdepth - depth + mindepth) 71 | ) 72 | ); 73 | var sum = 0; 74 | foreach (_ in 0..iterations) { 75 | final t = makeTree(depth); 76 | calHash(t); 77 | sum = sum + t.hash ?? 0; 78 | } 79 | 80 | std\print("{iterations}\t trees of depth {depth}\t root hash sum: {sum}"); 81 | } 82 | 83 | calHash(longlivedtree); 84 | std\print("long lived tree of depth {maxdepth}\t root hash: {longlivedtree.hash ?? 0} check: {check(longlivedtree)}"); 85 | } 86 | -------------------------------------------------------------------------------- /tests/bench/003-nbody.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | import "math"; 3 | 4 | final pi = 3.141592653589793; 5 | final solarMass = 4.0 * pi * pi; 6 | final daysPerYear = 365.24; 7 | 8 | object Body { 9 | x: double, 10 | y: double, 11 | z: double, 12 | vx: double, 13 | vy: double, 14 | vz: double, 15 | mass: double, 16 | } 17 | 18 | fun advance(bodies: [mut Body], nbody: int, dt: double) > void { 19 | foreach (i in 0..nbody) { 20 | final bi = bodies[i]; 21 | final bix = bi.x; 22 | final biy = bi.y; 23 | final biz = bi.z; 24 | final bimass = bi.mass; 25 | var bivx = bi.vx; 26 | var bivy = bi.vy; 27 | var bivz = bi.vz; 28 | 29 | foreach (j in (i + 1)..nbody) { 30 | final bj = bodies[j]; 31 | final dx = bix - bj.x; 32 | final dy = biy - bj.y; 33 | final dz = biz - bj.z; 34 | final dist2 = dx * dx + dy * dy + dz * dz; 35 | var mag = math\sqrt(dist2); 36 | mag = dt / (mag * dist2); 37 | var bm = bj.mass * mag; 38 | 39 | bivx = bivx - (dx * bm); 40 | bivy = bivy - (dy * bm); 41 | bivz = bivz - (dz * bm); 42 | bm = bimass * mag; 43 | bj.vx = bj.vx + (dx * bm); 44 | bj.vy = bj.vy + (dy * bm); 45 | bj.vz = bj.vz + (dz * bm); 46 | } 47 | 48 | bi.vx = bivx; 49 | bi.vy = bivy; 50 | bi.vz = bivz; 51 | bi.x = bix + dt * bivx; 52 | bi.y = biy + dt * bivy; 53 | bi.z = biz + dt * bivz; 54 | } 55 | } 56 | 57 | fun energy(bodies: [Body], nbody: int) > double { 58 | var e = 0.0; 59 | foreach (i in 0..nbody) { 60 | final bi = bodies[i]; 61 | final vx = bi.vx; 62 | final vy = bi.vy; 63 | final vz = bi.vz; 64 | final bim = bi.mass; 65 | e = e + (0.5 * bim * (vx * vx + vy * vy + vz * vz)); 66 | foreach (j in (i + 1)..nbody) { 67 | final bj = bodies[j]; 68 | final dx = bi.x - bj.x; 69 | final dy = bi.y - bj.y; 70 | final dz = bi.z - bj.z; 71 | final distance = math\sqrt(dx * dx + dy * dy + dz * dz); 72 | e = e - ((bim * bj.mass) / distance); 73 | } 74 | } 75 | 76 | return e; 77 | } 78 | 79 | fun offsetMomentum(b: [mut Body], nbody: int) > void { 80 | var px = 0.0; 81 | var py = 0.0; 82 | var pz = 0.0; 83 | 84 | foreach (i in 0..nbody) { 85 | final bi = b[i]; 86 | final bim = bi.mass; 87 | 88 | px = px + (bi.vx * bim); 89 | py = py + (bi.vy * bim); 90 | pz = pz + (bi.vz * bim); 91 | } 92 | 93 | b[0].vx = -px / solarMass; 94 | b[0].vy = -py / solarMass; 95 | b[0].vz = -pz / solarMass; 96 | } 97 | 98 | fun main(args: [str]) > void { 99 | final sun = mut mut Body{ 100 | x = 0.0, 101 | y = 0.0, 102 | z = 0.0, 103 | vx = 0.0, 104 | vy = 0.0, 105 | vz = 0.0, 106 | mass = solarMass, 107 | }; 108 | 109 | final jupiter = mut Body{ 110 | x = 4.84143144246472090, 111 | y = -1.16032004402742839, 112 | z = -0.103622044471123109, 113 | vx = 0.00166007664274403694 * daysPerYear, 114 | vy = 0.00769901118419740425 * daysPerYear, 115 | vz = -0.0000690460016972063023 * daysPerYear, 116 | mass = 0.000954791938424326609 * solarMass, 117 | }; 118 | 119 | final saturn = mut Body{ 120 | x = 8.34336671824457987, 121 | y = 4.12479856412430479, 122 | z = -0.403523417114321381, 123 | vx = -0.0027674251072686 * daysPerYear, 124 | vy = 0.0049985280123492 * daysPerYear, 125 | vz = 0.0000230417297573763929 * daysPerYear, 126 | mass = 0.00028588598066613 * solarMass, 127 | }; 128 | 129 | final uranus = mut Body{ 130 | x = 12.894369562139, 131 | y = -15.111151401699, 132 | z = -0.22330757889266, 133 | vx = 0.0029646013756476 * daysPerYear, 134 | vy = 0.0023784717395948 * daysPerYear, 135 | vz = -0.0000296589568540237556 * daysPerYear, 136 | mass = 0.0000436624404335156298 * solarMass, 137 | }; 138 | 139 | final neptune = mut Body{ 140 | x = 15.379697114851, 141 | y = -25.9193146099879641, 142 | z = 0.179258772950371181, 143 | vx = 0.00268067772490389322 * daysPerYear, 144 | vy = 0.00162824170038242295 * daysPerYear, 145 | vz = -0.0000951592254519715870 * daysPerYear, 146 | mass = 0.0000515138902046611451 * solarMass, 147 | }; 148 | 149 | final bodies = [ sun, jupiter, saturn, uranus, neptune ]; 150 | 151 | final N = std\parseInt(args[?0] ?? "1000") ?? 1000; 152 | final nbody = bodies.len(); 153 | 154 | offsetMomentum(bodies, nbody: nbody); 155 | std\print("{energy(bodies, nbody: nbody)}"); 156 | 157 | foreach (_ in 0..N) { 158 | advance(bodies, nbody: nbody, dt: 0.01); 159 | } 160 | 161 | std\print("{energy(bodies, nbody: nbody)}"); 162 | } 163 | -------------------------------------------------------------------------------- /tests/bench/004-spectral.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | import "math"; 3 | 4 | fun A(i: double, j: double) > double { 5 | final ij = i + j - 1.0; 6 | return 1.0 / (ij * (ij - 1.0) * 0.5 + i); 7 | } 8 | 9 | fun Av(x: [double], y: mut [double], N: int) > void { 10 | for (i: double = 0.0; i < N; i = i + 1.0) { 11 | var a = 0.0; 12 | for (j: double = 0.0; j < N; j = j + 1.0) { 13 | a = a + x[std\toInt(j)] * A(i: i, j: j); 14 | } 15 | y[std\toInt(i)] = a; 16 | } 17 | } 18 | 19 | fun Atv(x: [double], y: mut [double], N: int) > void { 20 | for (i: double = 0.0; i < N; i = i + 1.0) { 21 | var a = 0.0; 22 | for (j: double = 0.0; j < N; j = j + 1.0) { 23 | a = a + x[std\toInt(j)] * A(i: j, j: i); 24 | } 25 | y[std\toInt(i)] = a; 26 | } 27 | } 28 | 29 | fun AtAv(x: [double], y: mut [double], t: mut [double], N: int) > void { 30 | Av(x: x, y: t, N: N); 31 | Atv(x: t, y: y, N: N); 32 | } 33 | 34 | fun main(args: [str]) > void { 35 | final N = std\parseInt(args[?0] ?? "100") ?? 100; 36 | 37 | final u: mut [double] = mut []; 38 | final v: mut [double] = mut []; 39 | final t: mut [double] = mut []; 40 | foreach (_ in 0..N) { 41 | u.append(1.0); 42 | v.append(0.0); 43 | t.append(0.0); 44 | } 45 | 46 | foreach (_ in 0..10) { 47 | AtAv(x: u, y: v, t: t, N: N); 48 | AtAv(x: v, y: u, t: t, N: N); 49 | } 50 | 51 | var vBv = 0.0; 52 | var vv = 0.0; 53 | foreach (i in 0..N) { 54 | final ui = u[i]; 55 | final vi = v[i]; 56 | vBv = vBv + ui * vi; 57 | vv = vv + vi * vi; 58 | } 59 | 60 | std\print("{math\sqrt(vBv / vv)}"); 61 | } 62 | -------------------------------------------------------------------------------- /tests/bench/005-k-nucleoide.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | import "io"; 3 | import "errors"; 4 | 5 | fun readSequence() > str !> errors\ReadWriteError, errors\FileSystemError, errors\UnexpectedError { 6 | var skippedIgnoredSequences = false; 7 | final lines: mut [str] = mut []; 8 | for (line: str? = ""; line != null; line = io\stdin.readLine()) { // catch null doesn't work 9 | if (!skippedIgnoredSequences) { 10 | if (line?.startsWith(">THREE") ?? true) { 11 | skippedIgnoredSequences = true; 12 | } 13 | } else { 14 | final c = line?[0] ?? "_"; 15 | if (c == ">") { 16 | break; 17 | } else if (c != ";") { 18 | lines.append(line ?? ""); 19 | } 20 | } 21 | } 22 | 23 | return lines.join("").upper(); 24 | } 25 | 26 | fun kfrequency(sequence: str, frequency: mut {str: int}, k: int, frame: int) > void { 27 | for (i: int = frame; i < sequence.len(); i = i + k) { 28 | final c = sequence.sub(i, len: k); 29 | frequency[c] = (frequency[c] ?? 0) + 1; 30 | } 31 | } 32 | 33 | fun count(sequence: str, fragment: str) > void { 34 | final k = fragment.len(); 35 | final frequency: mut {str: int} = mut {}; 36 | for (frame: int = 0; frame < k; frame = frame + 1) { 37 | kfrequency(sequence, frequency: frequency, k: k, frame: frame); 38 | } 39 | std\print("{frequency[fragment] ?? 0}\t{fragment}"); 40 | } 41 | 42 | fun frequency(sequence: str, k: int) > void { 43 | final frequency: mut {str: int} = mut {}; 44 | for (frame: int = 0; frame < k; frame = frame + 1) { 45 | kfrequency(sequence, frequency: frequency, k: k, frame: frame); 46 | } 47 | 48 | final sum = sequence.len() - k + 1; 49 | foreach (c, f in frequency) { 50 | std\print("{c} {(f * 100) / sum}"); 51 | } 52 | std\print(""); 53 | } 54 | 55 | // buzz tests/bench/005-k-nucleoide.buzz < tests/bench/reference/knucleotide-input.txt 56 | fun main() > void { 57 | final sequence = readSequence() catch ""; 58 | frequency(sequence, k: 1); 59 | frequency(sequence, k: 2); 60 | count(sequence, fragment: "GGT"); 61 | count(sequence, fragment: "GGTA"); 62 | count(sequence, fragment: "GGTATT"); 63 | count(sequence, fragment: "GGTATTTTAATT"); 64 | count(sequence, fragment: "GGTATTTTAATTTATAGT"); 65 | } 66 | -------------------------------------------------------------------------------- /tests/bench/006-fasta.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | import "math"; 3 | import "buffer" as _; 4 | 5 | final IM = 139968.0; 6 | final IA = 3877.0; 7 | final IC = 29573.0; 8 | final lineLength = 60; 9 | final bufferSize = (lineLength + 1) * 1024; 10 | 11 | fun makeRepeatFasta(id: str, desc: str, s: str, nchars: int) > void { 12 | std\print(">{id} {desc}"); 13 | var p = 0; 14 | final sn = s.len(); 15 | final s2 = s + s; 16 | for (i: int = lineLength; i < nchars; i = i + lineLength) { 17 | std\print(s2.sub(p, len: lineLength - 1)); 18 | p = p + lineLength; 19 | if (p > sn) { 20 | p = p - sn; 21 | } 22 | } 23 | final tail = nchars % lineLength; 24 | if (tail > 0) { 25 | std\print(s2.sub(p, len: tail - 1)); 26 | } 27 | } 28 | 29 | object Frequency { 30 | chars: Buffer, 31 | probs: mut [double], 32 | last: double, 33 | 34 | static fun init(chars: Buffer, probs: mut [double], last: double) > mut Frequency { 35 | final freq = mut Frequency{ 36 | chars = chars, 37 | probs = probs, 38 | last = last, 39 | }; 40 | 41 | freq.makeCumulative(); 42 | 43 | return freq; 44 | } 45 | 46 | mut fun random(max: double) > double { 47 | this.last = (this.last * IA + IC) % IM; 48 | return max * this.last * (1.0 / IM); 49 | } 50 | 51 | mut fun makeCumulative() > void { 52 | var cp = 0.0; 53 | foreach (i, prob in this.probs) { 54 | cp = cp + prob; 55 | this.probs[i] = cp; 56 | } 57 | } 58 | 59 | mut fun selectRandomIntoBuffer(buffer: Buffer, initialBufferIndex: int, nRandom: int) > int !> OutOfBoundError, WriteWhileReadingError { 60 | final len = this.probs.len(); 61 | var bufferIndex = initialBufferIndex; 62 | 63 | foreach (_ in 0..nRandom) { 64 | final r = this.random(1.0); 65 | var skip = false; 66 | foreach (_ in 0..len) { 67 | if (r < this.probs[i]) { 68 | buffer.setAt(bufferIndex, value: this.chars.at(i)); 69 | bufferIndex = bufferIndex + 1; 70 | skip = true; 71 | break; 72 | } 73 | } 74 | 75 | if (!skip) { 76 | buffer.setAt(bufferIndex, value: this.chars.at(len - 1)); 77 | bufferIndex = bufferIndex + 1; 78 | } 79 | } 80 | 81 | return bufferIndex; 82 | } 83 | } 84 | 85 | fun makeRandomFasta(id: str, desc: str, fpf: mut Frequency, initialNchars: int) > void !> OutOfBoundError, WriteWhileReadingError { 86 | std\print(">{id} {desc}"); 87 | var nchars = initialNchars; 88 | 89 | var buffer = Buffer.init(bufferSize); 90 | var bufferIndex = 0; 91 | while (nchars > 0) { 92 | final chunkSize = std\toInt(math\minDouble(a: std\toDouble(lineLength), b: std\toDouble(nchars))); 93 | 94 | if (bufferIndex == bufferSize) { 95 | std\print(buffer.toString().sub(0, len: bufferIndex)); 96 | buffer = Buffer.init(bufferSize); 97 | bufferIndex = 0; 98 | } 99 | 100 | bufferIndex = fpf.selectRandomIntoBuffer(buffer, initialBufferIndex: bufferIndex, nRandom: chunkSize); 101 | buffer.setAt(bufferIndex, value: 10); 102 | bufferIndex = bufferIndex + 1; 103 | 104 | nchars = nchars - chunkSize; 105 | } 106 | 107 | std\print(buffer.toString().sub(0, len: bufferIndex)); 108 | } 109 | 110 | final alu = "GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG" 111 | + "GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA" 112 | + "CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT" 113 | + "ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA" 114 | + "GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG" 115 | + "AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC" 116 | + "AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA"; 117 | 118 | fun main(args: [str]) > void !> any { 119 | final N = std\parseInt(args[?0] ?? "1000") ?? 1000; 120 | 121 | makeRepeatFasta("ONE", desc: "Homo sapiens alu", s: alu, nchars: N * 2); 122 | 123 | final iubChars = Buffer.init(); 124 | iubChars.write("acgtBDHKMNRSVWY"); 125 | final iub = Frequency.init( 126 | chars: iubChars, 127 | probs: mut [ 0.27, 0.12, 0.12, 0.27, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.02 ], 128 | last: 42.0, 129 | ); 130 | 131 | makeRandomFasta("TWO", desc: "IUB ambiguity codes", fpf: iub, initialNchars: N * 3); 132 | 133 | final homoSapiensChars = Buffer.init(); 134 | homoSapiensChars.write("acgt"); 135 | final homoSapiens = Frequency.init( 136 | chars: homoSapiensChars, 137 | probs: mut [ 0.3029549426680, 0.1979883004921, 0.1975473066391, 0.3015094502008 ], 138 | last: iub.last, 139 | ); 140 | 141 | makeRandomFasta("THREE", desc: "Homo sapiens frequency", fpf: homoSapiens, initialNchars: N * 5); 142 | } 143 | -------------------------------------------------------------------------------- /tests/bench/007-fib.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | fun fibonnacci(n: int) > int { 4 | if (n < 2) { 5 | return n; 6 | } 7 | 8 | return fibonnacci(n - 2) + fibonnacci(n - 1); 9 | } 10 | 11 | fun main() > void { 12 | std\print("{fibonnacci(30)}"); 13 | } 14 | -------------------------------------------------------------------------------- /tests/bench/008-for.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | fun main() > void { 4 | final list: mut [int] = mut []; 5 | 6 | foreach (i in 0..1_000_000) { 7 | list.append(i); 8 | } 9 | 10 | var sum = 0; 11 | foreach (v in list) { 12 | sum = sum + v; 13 | } 14 | 15 | std\print("{sum}"); 16 | } 17 | -------------------------------------------------------------------------------- /tests/bench/009-grid.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | fun main(args: [str]) > void { 4 | final width = std\parseInt(args[?0] ?? "80") ?? 80; 5 | final height = std\parseInt(args[?1] ?? "60") ?? 60; 6 | 7 | final cells: mut [bool] = mut []; 8 | 9 | foreach (_ in 0..width * height) { 10 | cells.append(std\random(max: 5) == 1); 11 | } 12 | 13 | foreach (y in 0..height) { 14 | foreach (x in 0..width) { 15 | cells[y * width + x] = !cells[y * width + x]; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/bench/010-ackermann.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | fun main(args: [str]) > void { 4 | final m = std\parseInt(args[?0] ?? "3") ?? 3; 5 | final n = std\parseInt(args[?1] ?? "8") ?? 8; 6 | 7 | std\print("result: {ack(m, n)}"); 8 | } 9 | 10 | fun ack(m: int, n: int) > int { 11 | if (m == 0) { 12 | return n + 1; 13 | } 14 | 15 | if (n == 0) { 16 | return ack(m: m - 1, n: 1); 17 | } 18 | 19 | return ack( 20 | m: m - 1, 21 | n: ack(m, n: n - 1) 22 | ); 23 | } 24 | -------------------------------------------------------------------------------- /tests/bench/011-bubble-sort.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | fun main(args: [str]) > void { 4 | final max = std\parseInt(args[?0] ?? "750") ?? 750; 5 | 6 | var list = init(max); 7 | 8 | bubblesort(list); 9 | 10 | foreach (e in list) { 11 | std\print("{e}"); 12 | } 13 | } 14 | 15 | fun init(max: int) > [int] { 16 | final list: [int] = []; 17 | final f = max - 13; 18 | final h = ((max - 117) * (max - 13)) / max; 19 | 20 | foreach (i in 0..max) { 21 | list.append((f * i) % h - (h / 2)); 22 | } 23 | 24 | return list; 25 | } 26 | 27 | fun bubblesort(list: [int]) > void { 28 | final len = list.len(); 29 | foreach (_ in 0..(len - 1)) { 30 | foreach (j in 0..(len - 1)) { 31 | if (list[j] > list[j + 1]) { 32 | final tmp = list[j]; 33 | list[j] = list[j + 1]; 34 | list[j + 1] = tmp; 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/bench/README.md: -------------------------------------------------------------------------------- 1 | # Benches 2 | 3 | See: 4 | - https://sschakraborty.github.io/benchmark/lua.html 5 | - https://programming-language-benchmarks.vercel.app/ -------------------------------------------------------------------------------- /tests/bench/reference/btree.lua: -------------------------------------------------------------------------------- 1 | -- The Computer Language Benchmarks Game 2 | -- http://benchmarksgame.alioth.debian.org/ 3 | -- contributed by Mike Pall 4 | -- *reset* 5 | 6 | local function BottomUpTree(depth) 7 | if depth > 0 then 8 | depth = depth - 1 9 | local left, right = BottomUpTree(depth), BottomUpTree(depth) 10 | return { left, right } 11 | else 12 | return {} 13 | end 14 | end 15 | 16 | local function ItemCheck(tree) 17 | if tree[1] then 18 | return 1 + ItemCheck(tree[1]) + ItemCheck(tree[2]) 19 | else 20 | return 1 21 | end 22 | end 23 | 24 | local N = tonumber(arg and arg[1]) or 0 25 | local mindepth = 4 26 | local maxdepth = mindepth + 2 27 | if maxdepth < N then maxdepth = N end 28 | 29 | do 30 | local stretchdepth = maxdepth + 1 31 | local stretchtree = BottomUpTree(stretchdepth) 32 | io.write(string.format("stretch tree of depth %d\t check: %d\n", 33 | stretchdepth, ItemCheck(stretchtree))) 34 | end 35 | 36 | local longlivedtree = BottomUpTree(maxdepth) 37 | 38 | for depth = mindepth, maxdepth, 2 do 39 | local iterations = 2 ^ (maxdepth - depth + mindepth) 40 | local check = 0 41 | for i = 1, iterations do 42 | check = check + ItemCheck(BottomUpTree(depth)) 43 | end 44 | io.write(string.format("%d\t trees of depth %d\t check: %d\n", 45 | iterations, depth, check)) 46 | end 47 | 48 | io.write(string.format("long lived tree of depth %d\t check: %d\n", 49 | maxdepth, ItemCheck(longlivedtree))) 50 | -------------------------------------------------------------------------------- /tests/bench/reference/fasta.dart: -------------------------------------------------------------------------------- 1 | /* The Computer Language Benchmarks Game 2 | http://benchmarksgame.alioth.debian.org/ 3 | 4 | contributed by James Wendel 5 | */ 6 | 7 | import 'dart:io'; 8 | import 'dart:typed_data'; 9 | 10 | const String ALU = "GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG" 11 | "GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA" 12 | "CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT" 13 | "ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA" 14 | "GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG" 15 | "AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC" 16 | "AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA"; 17 | 18 | final Frequency IUB = new Frequency([ 19 | 'a', 20 | 'c', 21 | 'g', 22 | 't', 23 | 'B', 24 | 'D', 25 | 'H', 26 | 'K', 27 | 'M', 28 | 'N', 29 | 'R', 30 | 'S', 31 | 'V', 32 | 'W', 33 | 'Y' 34 | ], [ 35 | 0.27, 36 | 0.12, 37 | 0.12, 38 | 0.27, 39 | 0.02, 40 | 0.02, 41 | 0.02, 42 | 0.02, 43 | 0.02, 44 | 0.02, 45 | 0.02, 46 | 0.02, 47 | 0.02, 48 | 0.02, 49 | 0.02, 50 | ]); 51 | 52 | final Frequency HOMO_SAPIENS = 53 | new Frequency(['a', 'c', 'g', 't'], [0.3029549426680, 0.1979883004921, 0.1975473066391, 0.3015094502008]); 54 | 55 | const int IM = 139968; 56 | const int IA = 3877; 57 | const int IC = 29573; 58 | 59 | const int LINE_LENGTH = 60; 60 | const int BUFFER_SIZE = (LINE_LENGTH + 1) * 1024; 61 | 62 | const double oneOverIM = (1.0 / IM); 63 | 64 | class Frequency { 65 | late Uint8List chars; 66 | late Float64List probs; 67 | late int last; 68 | 69 | double random(double max) { 70 | last = (last * IA + IC) % IM; 71 | return max * last * oneOverIM; 72 | } 73 | 74 | Frequency(List charList, List probList) { 75 | chars = new Uint8List(charList.length); 76 | for (int i = 0; i < chars.length; i++) { 77 | chars[i] = charList[i].codeUnitAt(0); 78 | } 79 | 80 | probs = new Float64List(probList.length); 81 | for (int i = 0; i < probList.length; i++) { 82 | probs[i] = probList[i]; 83 | } 84 | 85 | makeCumulative(); 86 | } 87 | 88 | void makeCumulative() { 89 | double cp = 0.0; 90 | for (int i = 0; i < probs.length; i++) { 91 | cp += probs[i]; 92 | probs[i] = cp; 93 | } 94 | } 95 | 96 | int selectRandomIntoBuffer(Uint8List buffer, int bufferIndex, int nRandom) { 97 | final int len = probs.length; 98 | 99 | outer: 100 | for (int rIndex = 0; rIndex < nRandom; rIndex++) { 101 | double r = random(1.0); 102 | for (int i = 0; i < len; i++) { 103 | if (r < probs[i]) { 104 | buffer[bufferIndex++] = chars[i]; 105 | continue outer; 106 | } 107 | } 108 | 109 | buffer[bufferIndex++] = chars[len - 1]; 110 | } 111 | 112 | return bufferIndex; 113 | } 114 | } 115 | 116 | makeRepeatFasta(String id, String desc, String alu, int _nChars, IOSink writer) { 117 | writer.write(">${id} ${desc}\n"); 118 | 119 | int aluIndex = 0; 120 | final List aluCode = alu.codeUnits; 121 | final int aluLength = aluCode.length; 122 | 123 | Uint8List buffer = new Uint8List(BUFFER_SIZE); 124 | 125 | int bufferIndex = 0; 126 | int nChars = _nChars; 127 | while (nChars > 0) { 128 | final int chunkSize = nChars >= LINE_LENGTH ? LINE_LENGTH : nChars; 129 | 130 | if (bufferIndex == BUFFER_SIZE) { 131 | writer.add(new Uint8List.view(buffer.buffer, 0, bufferIndex)); 132 | buffer = new Uint8List(BUFFER_SIZE); 133 | bufferIndex = 0; 134 | } 135 | 136 | if (aluIndex + chunkSize < aluLength) { 137 | buffer.setRange(bufferIndex, bufferIndex + chunkSize, aluCode, aluIndex); 138 | bufferIndex += chunkSize; 139 | aluIndex += chunkSize; 140 | } else { 141 | int len = aluLength - aluIndex; 142 | buffer.setRange(bufferIndex, bufferIndex + len, aluCode, aluIndex); 143 | bufferIndex += len; 144 | aluIndex = 0; 145 | len = chunkSize - len; 146 | buffer.setRange(bufferIndex, bufferIndex + len, aluCode, aluIndex); 147 | bufferIndex += len; 148 | aluIndex += len; 149 | } 150 | 151 | buffer[bufferIndex++] = 10; 152 | 153 | nChars -= chunkSize; 154 | } 155 | 156 | writer.add(new Uint8List.view(buffer.buffer, 0, bufferIndex)); 157 | } 158 | 159 | void makeRandomFasta(String id, String desc, Frequency fpf, int nChars, IOSink writer) { 160 | writer.write(">${id} ${desc}\n"); 161 | 162 | Uint8List buffer = new Uint8List(BUFFER_SIZE); 163 | 164 | int bufferIndex = 0; 165 | while (nChars > 0) { 166 | final int chunkSize = nChars >= LINE_LENGTH ? LINE_LENGTH : nChars; 167 | 168 | if (bufferIndex == BUFFER_SIZE) { 169 | writer.add(new Uint8List.view(buffer.buffer, 0, bufferIndex)); 170 | buffer = new Uint8List(BUFFER_SIZE); 171 | bufferIndex = 0; 172 | } 173 | 174 | bufferIndex = fpf.selectRandomIntoBuffer(buffer, bufferIndex, chunkSize); 175 | buffer[bufferIndex++] = 10; 176 | 177 | nChars -= chunkSize; 178 | } 179 | 180 | writer.add(new Uint8List.view(buffer.buffer, 0, bufferIndex)); 181 | } 182 | 183 | main(args) { 184 | IOSink writer = stdout; 185 | 186 | int n = args.length > 0 ? int.parse(args[0]) : 1000; 187 | 188 | makeRepeatFasta("ONE", "Homo sapiens alu", ALU, n * 2, writer); 189 | IUB.last = 42; 190 | makeRandomFasta("TWO", "IUB ambiguity codes", IUB, n * 3, writer); 191 | HOMO_SAPIENS.last = IUB.last; 192 | makeRandomFasta("THREE", "Homo sapiens frequency", HOMO_SAPIENS, n * 5, writer); 193 | } 194 | -------------------------------------------------------------------------------- /tests/bench/reference/fasta.lua: -------------------------------------------------------------------------------- 1 | -- The Computer Language Benchmarks Game 2 | -- http://benchmarksgame.alioth.debian.org/ 3 | -- contributed by Mike Pall 4 | -- modified for 5.3 by Robin 5 | 6 | local Last = 42 7 | local function random(max) 8 | local y = (Last * 3877 + 29573) % 139968 9 | Last = y 10 | return (max * y) / 139968 11 | end 12 | 13 | local function make_repeat_fasta(id, desc, s, n) 14 | local write, sub = io.write, string.sub 15 | write(">", id, " ", desc, "\n") 16 | local p, sn, s2 = 1, #s, s .. s 17 | for i = 60, n, 60 do 18 | write(sub(s2, p, p + 59), "\n") 19 | p = p + 60; 20 | if p > sn then p = p - sn end 21 | end 22 | local tail = n % 60 23 | if tail > 0 then write(sub(s2, p, p + tail - 1), "\n") end 24 | end 25 | 26 | local function make_random_fasta(id, desc, bs, n) 27 | io.write(">", id, " ", desc, "\n") 28 | load([=[ 29 | local write, char, unpack, n, random = io.write, string.char, table.unpack, ... 30 | local buf, p = {}, 1 31 | for i=60,n,60 do 32 | for j=p,p+59 do ]=] .. bs .. [=[ end 33 | buf[p+60] = 10; p = p + 61 34 | if p >= 2048 then write(char(unpack(buf, 1, p-1))); p = 1 end 35 | end 36 | local tail = n % 60 37 | if tail > 0 then 38 | for j=p,p+tail-1 do ]=] .. bs .. [=[ end 39 | p = p + tail; buf[p] = 10; p = p + 1 40 | end 41 | write(char(unpack(buf, 1, p-1))) 42 | ]=], desc)(n, random) 43 | end 44 | 45 | local function bisect(c, p, lo, hi) 46 | local n = hi - lo 47 | if n == 0 then return "buf[j] = " .. c[hi] .. "\n" end 48 | local mid = math.floor(n / 2) 49 | return "if r < " .. p[lo + mid] .. " then\n" .. bisect(c, p, lo, lo + mid) .. 50 | "else\n" .. bisect(c, p, lo + mid + 1, hi) .. "end\n" 51 | end 52 | 53 | local function make_bisect(tab) 54 | local c, p, sum = {}, {}, 0 55 | for i, row in ipairs(tab) do 56 | c[i] = string.byte(row[1]) 57 | sum = sum + row[2] 58 | p[i] = sum 59 | end 60 | return "local r = random(1)\n" .. bisect(c, p, 1, #tab) 61 | end 62 | 63 | local alu = 64 | "GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG" .. 65 | "GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA" .. 66 | "CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT" .. 67 | "ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA" .. 68 | "GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG" .. 69 | "AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC" .. 70 | "AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA" 71 | 72 | local iub = make_bisect { 73 | { "a", 0.27 }, 74 | { "c", 0.12 }, 75 | { "g", 0.12 }, 76 | { "t", 0.27 }, 77 | { "B", 0.02 }, 78 | { "D", 0.02 }, 79 | { "H", 0.02 }, 80 | { "K", 0.02 }, 81 | { "M", 0.02 }, 82 | { "N", 0.02 }, 83 | { "R", 0.02 }, 84 | { "S", 0.02 }, 85 | { "V", 0.02 }, 86 | { "W", 0.02 }, 87 | { "Y", 0.02 }, 88 | } 89 | 90 | local homosapiens = make_bisect { 91 | { "a", 0.3029549426680 }, 92 | { "c", 0.1979883004921 }, 93 | { "g", 0.1975473066391 }, 94 | { "t", 0.3015094502008 }, 95 | } 96 | 97 | -- lua fasta.lua 25000000 98 | local N = tonumber(arg and arg[1]) or 1000 99 | make_repeat_fasta('ONE', 'Homo sapiens alu', alu, N * 2) 100 | make_random_fasta('TWO', 'IUB ambiguity codes', iub, N * 3) 101 | make_random_fasta('THREE', 'Homo sapiens frequency', homosapiens, N * 5) 102 | -------------------------------------------------------------------------------- /tests/bench/reference/grid.lua: -------------------------------------------------------------------------------- 1 | local width = tonumber(arg and arg[1]) or 80 2 | local height = tonumber(arg and arg[2]) or 60 3 | 4 | local cells = {} 5 | 6 | for _ = 1, width * height do 7 | table.insert(cells, math.random(5) == 1) 8 | end 9 | 10 | for y = 1, height do 11 | for x = 1, width do 12 | cells[y * width + x] = not cells[y * width + x]; 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /tests/bench/reference/k-nucleoide.lua: -------------------------------------------------------------------------------- 1 | -- The Computer Language Benchmarks Game 2 | -- http://benchmarksgame.alioth.debian.org/ 3 | -- contributed by Mike Pall 4 | 5 | local function kfrequency(seq, freq, k, frame) 6 | local sub = string.sub 7 | local k1 = k - 1 8 | for i = frame, string.len(seq) - k1, k do 9 | local c = sub(seq, i, i + k1) 10 | freq[c] = freq[c] + 1 11 | end 12 | end 13 | 14 | local function freqdefault() 15 | return 0 16 | end 17 | 18 | local function count(seq, frag) 19 | local k = string.len(frag) 20 | local freq = setmetatable({}, { __index = freqdefault }) 21 | for frame = 1, k do kfrequency(seq, freq, k, frame) end 22 | io.write(freq[frag] or 0, "\t", frag, "\n") 23 | end 24 | 25 | local function frequency(seq, k) 26 | local freq = setmetatable({}, { __index = freqdefault }) 27 | for frame = 1, k do kfrequency(seq, freq, k, frame) end 28 | local sfreq, sn = {}, 1 29 | for c, v in pairs(freq) do sfreq[sn] = c; 30 | sn = sn + 1 31 | end 32 | table.sort(sfreq, function(a, b) 33 | local fa, fb = freq[a], freq[b] 34 | return fa == fb and a > b or fa > fb 35 | end) 36 | local sum = string.len(seq) - k + 1 37 | for _, c in ipairs(sfreq) do 38 | io.write(string.format("%s %0.3f\n", c, (freq[c] * 100) / sum)) 39 | end 40 | io.write("\n") 41 | end 42 | 43 | local function readseq() 44 | local sub = string.sub 45 | for line in io.lines() do 46 | if sub(line, 1, 1) == ">" and sub(line, 2, 6) == "THREE" then break end 47 | end 48 | local lines, ln = {}, 0 49 | for line in io.lines() do 50 | local c = sub(line, 1, 1) 51 | if c == ">" then 52 | break 53 | elseif c ~= ";" then 54 | ln = ln + 1 55 | lines[ln] = line 56 | end 57 | end 58 | return string.upper(table.concat(lines, "", 1, ln)) 59 | end 60 | 61 | local seq = readseq() 62 | frequency(seq, 1) 63 | frequency(seq, 2) 64 | count(seq, "GGT") 65 | count(seq, "GGTA") 66 | count(seq, "GGTATT") 67 | count(seq, "GGTATTTTAATT") 68 | count(seq, "GGTATTTTAATTTATAGT") 69 | -------------------------------------------------------------------------------- /tests/bench/reference/knucleotide-output.txt: -------------------------------------------------------------------------------- 1 | T 31.520 2 | A 29.600 3 | C 19.480 4 | G 19.400 5 | 6 | AT 9.922 7 | TT 9.602 8 | TA 9.402 9 | AA 8.402 10 | GA 6.321 11 | TC 6.301 12 | TG 6.201 13 | GT 6.041 14 | CT 5.961 15 | AG 5.841 16 | CA 5.461 17 | AC 5.441 18 | CC 4.041 19 | CG 4.021 20 | GC 3.701 21 | GG 3.341 22 | 23 | 54 GGT 24 | 24 GGTA 25 | 4 GGTATT 26 | 0 GGTATTTTAATT 27 | 0 GGTATTTTAATTTATAGT 28 | -------------------------------------------------------------------------------- /tests/compile_errors/001-const.buzz: -------------------------------------------------------------------------------- 1 | // Can't assign to final variable 2 | import "std"; 3 | 4 | test "`final` variable" { 5 | final yo = "yo"; 6 | 7 | yo = "no way"; 8 | } 9 | 10 | // function arguments are `final` 11 | fun illegal(yo: str) > void { 12 | yo = "no way"; 13 | } 14 | 15 | enum Hey { 16 | lo, 17 | joe, 18 | } 19 | 20 | test "assigning to enum field" { 21 | Hey.lo = 1; 22 | } 23 | -------------------------------------------------------------------------------- /tests/compile_errors/002-break.buzz: -------------------------------------------------------------------------------- 1 | // break is not allowed here. 2 | test "break outside of loop" { 3 | while (true) { 4 | break; 5 | } 6 | 7 | if (true) { 8 | break; 9 | } 10 | 11 | break; 12 | } -------------------------------------------------------------------------------- /tests/compile_errors/004-not-callable.buzz: -------------------------------------------------------------------------------- 1 | // Can't be called 2 | test "Can't be called" { 3 | final yo = 12; 4 | yo("mo"); 5 | } 6 | -------------------------------------------------------------------------------- /tests/compile_errors/005-bad-named-variable-value.buzz: -------------------------------------------------------------------------------- 1 | // Bad value type: got type `int`, expected `Yo` 2 | object Yo { 3 | name: str, 4 | } 5 | 6 | test "Wrong named variable value" { 7 | var yo = Yo{ name = "yolo" }; 8 | 9 | yo = 12; 10 | } 11 | -------------------------------------------------------------------------------- /tests/compile_errors/006-deep-yield.buzz: -------------------------------------------------------------------------------- 1 | // Type mismatch: got type `str`, expected `void` 2 | import "std"; 3 | 4 | fun one() > void *> str? { 5 | _ = yield "hello"; 6 | } 7 | 8 | fun two() > void { 9 | one(); 10 | } 11 | 12 | test "Deep yield" { 13 | final fiber = &two(); 14 | std\print(resume fiber ?? "nope"); 15 | } 16 | -------------------------------------------------------------------------------- /tests/compile_errors/007-yield-location.buzz: -------------------------------------------------------------------------------- 1 | // Can't yield here 2 | test "Bad yield" { 3 | _ = yield "yolo"; 4 | } 5 | -------------------------------------------------------------------------------- /tests/compile_errors/008-error-message.buzz: -------------------------------------------------------------------------------- 1 | // Hello world 2 | fun fail() > void !> obj{ message: str } { 3 | throw .{ message = "Hello world" }; 4 | } 5 | 6 | fun depth() > void !> obj{ message: str } { 7 | fail(); 8 | } 9 | 10 | test "Throwing an object instance with a message field" { 11 | depth(); 12 | } 13 | -------------------------------------------------------------------------------- /tests/compile_errors/009-bad-named-variable-value.buzz: -------------------------------------------------------------------------------- 1 | // Bad value type: got type `int`, expected `str` 2 | test "bad named variable value" { 3 | var hello = "hello"; 4 | 5 | hello = 12; 6 | 7 | _: [int] = [ 8 | true, 9 | 12, 10 | ]; 11 | } 12 | -------------------------------------------------------------------------------- /tests/compile_errors/010-multiple-location.buzz: -------------------------------------------------------------------------------- 1 | // Property `name` does not exists 2 | import "errors"; 3 | 4 | object Some { 5 | msg: str, 6 | } 7 | 8 | test "Badly initializing an object declared in another file" { 9 | _ = errors\CompileError{ 10 | name = "wtf", 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /tests/compile_errors/011-protocol.buzz: -------------------------------------------------------------------------------- 1 | // Object declared as conforming to protocol `Living` but doesn't implement method `live` 2 | protocol Living { 3 | fun live() > void; 4 | } 5 | 6 | object Ant { 7 | name: str, 8 | } 9 | 10 | test "Unimplemented protocol method" { 11 | _ = Ant{ 12 | name = "joe" 13 | }; 14 | } 15 | -------------------------------------------------------------------------------- /tests/compile_errors/012-collect-signature.buzz: -------------------------------------------------------------------------------- 1 | // Expected `collect` method to be `fun collect() > void` got fun collect(_: str) > void 2 | object Collectable { 3 | fun collect(_: str) > void {} 4 | } 5 | -------------------------------------------------------------------------------- /tests/compile_errors/013-tostring-signature.buzz: -------------------------------------------------------------------------------- 1 | // Expected `toString` method to be `fun toString() > str` got fun toString(_: str) > void 2 | object Printable { 3 | fun toString(_: str) > void {} 4 | } 5 | -------------------------------------------------------------------------------- /tests/compile_errors/014-main-signature.buzz: -------------------------------------------------------------------------------- 1 | // Expected `main` signature to be `fun main([ args: [int] ]) > void|int` got fun main(args: int) > void 2 | fun main(args: int) > void {} 3 | -------------------------------------------------------------------------------- /tests/compile_errors/015-bad-pattern.buzz: -------------------------------------------------------------------------------- 1 | // Could not compile pattern 2 | import "std"; 3 | 4 | test "bad pattern" { 5 | final bad = $"bad\pattern"; 6 | } 7 | -------------------------------------------------------------------------------- /tests/compile_errors/016-unused-local.buzz: -------------------------------------------------------------------------------- 1 | // Unused local of type `int` 2 | import "std"; 3 | 4 | fun hey(unused: int) > void { 5 | print("hey you missed me"); 6 | } 7 | 8 | test "discarded value" { 9 | print("hello world"); 10 | 11 | "forgot me?"; 12 | 13 | final unused = 42; 14 | } 15 | -------------------------------------------------------------------------------- /tests/compile_errors/017-invalid-int-literal.buzz: -------------------------------------------------------------------------------- 1 | // '_' must be between digits 2 | test "invalid literals" { 3 | _ = 100_; 4 | } 5 | -------------------------------------------------------------------------------- /tests/compile_errors/018-invalid-float-literal.buzz: -------------------------------------------------------------------------------- 1 | // '_' must be between digits 2 | test "invalid literals" { 3 | _ = 100.2_; 4 | } 5 | -------------------------------------------------------------------------------- /tests/compile_errors/019-invalid-int-hex-literal.buzz: -------------------------------------------------------------------------------- 1 | // '_' must be between digits 2 | test "invalid hex literal" { 3 | _ = 0x_100; 4 | } 5 | -------------------------------------------------------------------------------- /tests/compile_errors/020-invalid-int-bin-literal.buzz: -------------------------------------------------------------------------------- 1 | // '_' must be between digits 2 | test "invalid bin literal" { 3 | _ = 0b_100; 4 | } 5 | -------------------------------------------------------------------------------- /tests/compile_errors/021-fiber-error-location.buzz: -------------------------------------------------------------------------------- 1 | //:7:37 2 | import "std"; 3 | 4 | fun count(n: int) > str *> int? { 5 | for (i: int = 0; i < n; i = i + 1) { 6 | if (i == 2) { 7 | throw "an error occured"; 8 | } 9 | _ = yield i; 10 | } 11 | 12 | return "Counting is done!"; 13 | } 14 | 15 | fun run() > void { 16 | final counter = &count(10); 17 | 18 | var sum = 0; 19 | while (!counter.over()) { 20 | sum = sum + resume counter ?? 0; 21 | } 22 | 23 | std\print(resolve counter); 24 | } 25 | 26 | test "fiber error location" { 27 | run(); 28 | } 29 | -------------------------------------------------------------------------------- /tests/compile_errors/022-var-decl-without-value.buzz: -------------------------------------------------------------------------------- 1 | // Expected variable initial value 2 | test "Var decl without a value" { 3 | final canidothis: str; 4 | } 5 | -------------------------------------------------------------------------------- /tests/compile_errors/023-empty-import.buzz: -------------------------------------------------------------------------------- 1 | // Import path can't be empty 2 | import ""; 3 | 4 | test "empty import" {} -------------------------------------------------------------------------------- /tests/compile_errors/024-misplaced-out.buzz: -------------------------------------------------------------------------------- 1 | // `out` statement is only allowed inside a block expression 2 | test "misplaced out statement" { 3 | out "not allowed"; 4 | } -------------------------------------------------------------------------------- /tests/compile_errors/025-multiple-out.buzz: -------------------------------------------------------------------------------- 1 | // Only one `out` statement is allowed in block expression 2 | test "multiple out statements" { 3 | from { 4 | out "one"; 5 | out "two"; 6 | }; 7 | } -------------------------------------------------------------------------------- /tests/compile_errors/026-out-last-statement.buzz: -------------------------------------------------------------------------------- 1 | // Last block expression statement must be `out` 2 | test "out must be last statement" { 3 | from { 4 | out "i should be last"; 5 | 6 | "hello world"; 7 | }; 8 | } -------------------------------------------------------------------------------- /tests/compile_errors/027-early-return.buzz: -------------------------------------------------------------------------------- 1 | // Code after return statement will never be reached 2 | import "std"; 3 | 4 | test "Early return" { 5 | std\print("I sure hope i'm not interrupted..."); 6 | 7 | return; 8 | 9 | std\print("Guess I was"); 10 | } -------------------------------------------------------------------------------- /tests/compile_errors/028-unused-import.buzz: -------------------------------------------------------------------------------- 1 | // Unused import 2 | import "math"; 3 | import "std"; 4 | 5 | test "Doing nothing with the math import" { 6 | std\print("hello"); 7 | } -------------------------------------------------------------------------------- /tests/compile_errors/029-label-not-found.buzz: -------------------------------------------------------------------------------- 1 | // Label `somewhere` does not exists. 2 | test "using a label that does not exists" { 3 | while (true) { 4 | break somewhere; 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tests/compile_errors/030-final-objects.buzz: -------------------------------------------------------------------------------- 1 | // `age` is final 2 | object Constant { 3 | final name: str, 4 | final age: int, 5 | } 6 | 7 | object PartiallyConstant { 8 | final name: str, 9 | age: int, 10 | } 11 | 12 | test "Constant object" { 13 | final constant = Constant{ 14 | name = "Joe", 15 | age = 35, 16 | }; 17 | 18 | constant.age = 38; 19 | } 20 | -------------------------------------------------------------------------------- /tests/compile_errors/031-long-tuple.buzz: -------------------------------------------------------------------------------- 1 | // Tuples can't have more than 4 elements 2 | test "Long tuple" { 3 | .{ 1, 2, 3, 4, 5 }; 4 | } 5 | -------------------------------------------------------------------------------- /tests/compile_errors/032-tuple-mix.buzz: -------------------------------------------------------------------------------- 1 | // Can't mix tuple notation and regular properties in anonymous object initialization 2 | test "Mixing tuple notation and regular anonymous object" { 3 | .{ name = "Joe", 42 }; 4 | } 5 | -------------------------------------------------------------------------------- /tests/compile_errors/033-immutable-list.buzz: -------------------------------------------------------------------------------- 1 | // Method `append` requires mutable list 2 | test "Trying to modify immutable value" { 3 | final list = [1, 2, 3]; 4 | 5 | // Means a method has to define if its instance is mutable or not! 6 | // We might need https://github.com/buzz-language/buzz/issues/98 after all! 7 | list.append(4); // Not allowed 8 | } 9 | 10 | -------------------------------------------------------------------------------- /tests/compile_errors/034-var-not-assigned.buzz: -------------------------------------------------------------------------------- 1 | // Local `m` is declared `var` but is never assigned 2 | fun h() > void { 3 | var m = []; 4 | _ = m; 5 | } 6 | 7 | -------------------------------------------------------------------------------- /tests/compile_errors/utils/bad-import.buzz: -------------------------------------------------------------------------------- 1 | fun yeah() > void { 2 | print(hello); 3 | } -------------------------------------------------------------------------------- /tests/manual/001-cli-args.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | 3 | fun main(args: [str]) > void { 4 | foreach (index, arg in args) { 5 | std\print("{index}: {arg}"); 6 | } 7 | } -------------------------------------------------------------------------------- /tests/manual/002-error.buzz: -------------------------------------------------------------------------------- 1 | fun main() > void !> any { 2 | throw "wat!"; 3 | } 4 | -------------------------------------------------------------------------------- /tests/manual/003-io.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | import "io"; 3 | 4 | fun main() > void !> any { 5 | for (line: str? = ""; line != null; line = io\stdin.readLine()) { 6 | std\print("= {line}"); 7 | io\stdout.write("> "); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/manual/004-os.buzz: -------------------------------------------------------------------------------- 1 | import "os"; 2 | 3 | fun main() > void { 4 | os\exit(12); 5 | } 6 | -------------------------------------------------------------------------------- /tests/manual/005-tcp-client.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | import "os"; 3 | 4 | fun main() > void !> any { 5 | final socket = os\Socket.connect( 6 | address: "127.0.0.1", 7 | port: 8080, 8 | netProtocol: os\SocketProtocol.tcp 9 | ); 10 | 11 | std\print("socket fd: {socket.fd}"); 12 | 13 | socket.send("hello world"); 14 | 15 | socket.close(); 16 | } 17 | -------------------------------------------------------------------------------- /tests/manual/006-http-client.buzz: -------------------------------------------------------------------------------- 1 | import "http"; 2 | import "debug"; 3 | import "errors"; 4 | import "serialize"; 5 | 6 | fun main() > void !> any { 7 | final client = http\Client.init(); 8 | 9 | final request = mut http\Request{ 10 | method = http\Method.GET, 11 | headers = { 12 | "accept": "*/*", 13 | }, 14 | uri = "https://staging.web-fr-front-19.boursorama.com", 15 | }; 16 | 17 | final response = client.send(request); 18 | debug\dump(response.body ?? "none"); 19 | // final fact = serialize\jsonDecode(response.body ?? "null").q(["fact"]).stringValue(); 20 | 21 | // debug\dump(fact); 22 | } 23 | -------------------------------------------------------------------------------- /tests/manual/007-fd-poller.buzz: -------------------------------------------------------------------------------- 1 | import "std"; 2 | import "io"; 3 | 4 | fun main() > void !> any { 5 | final poller = io\stdin.getPoller(); 6 | 7 | while (true) { 8 | if (poller.poll(100) -> polled) { 9 | std\print("> {polled}"); 10 | 11 | if (polled == "q") { 12 | break; 13 | } 14 | } else { 15 | std\print("nothing..."); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/utils/foreign.zig: -------------------------------------------------------------------------------- 1 | const std = @import("std"); 2 | 3 | export fn acos(value: f64) callconv(.c) f64 { 4 | return std.math.acos(value); 5 | } 6 | 7 | export fn fprint(msg: [*:0]const u8) callconv(.c) void { 8 | std.debug.print("{s}\n", .{msg}); 9 | } 10 | 11 | export fn sum(values: [*]i32, len: i32) callconv(.c) i32 { 12 | var total: i32 = 0; 13 | for (0..@intCast(len)) |i| { 14 | total += values[i]; 15 | } 16 | 17 | return total; 18 | } 19 | 20 | pub const Data = extern struct { 21 | id: i32, 22 | msg: [*:0]u8, 23 | value: f64, 24 | }; 25 | 26 | export fn get_data_msg(data: *Data) callconv(.c) [*:0]u8 { 27 | return data.msg; 28 | } 29 | 30 | export fn set_data_id(data: *Data) callconv(.c) void { 31 | data.id *= 2; 32 | } 33 | 34 | pub const Flag = extern struct { 35 | id: i32, 36 | value: bool, 37 | }; 38 | 39 | pub const Misc = extern union { 40 | id: i32, 41 | data: Data, 42 | flag: Flag, 43 | }; 44 | 45 | export fn get_misc_msg(misc: *Misc) callconv(.c) [*:0]u8 { 46 | return misc.data.msg; 47 | } 48 | 49 | export fn get_misc_flag(misc: *Misc) callconv(.c) bool { 50 | std.debug.print( 51 | "> {x:0>2}\n", 52 | .{ 53 | std.mem.asBytes(misc), 54 | }, 55 | ); 56 | return misc.flag.value; 57 | } 58 | 59 | export fn set_misc_id(misc: *Misc, new_id: i32) callconv(.c) void { 60 | misc.id = new_id; 61 | } 62 | -------------------------------------------------------------------------------- /tests/utils/import-a.buzz: -------------------------------------------------------------------------------- 1 | namespace a; 2 | 3 | object Hello { 4 | message: str, 5 | } 6 | 7 | export Hello; 8 | -------------------------------------------------------------------------------- /tests/utils/import-b.buzz: -------------------------------------------------------------------------------- 1 | namespace b; 2 | 3 | import "tests/utils/import-a"; 4 | import "std"; 5 | 6 | var hello = a\Hello{ message = "hello world" }; 7 | 8 | fun printClass() > void { 9 | std\print("b: {a\Hello}"); 10 | } 11 | 12 | export hello; 13 | export printClass; 14 | -------------------------------------------------------------------------------- /tests/utils/testing.buzz: -------------------------------------------------------------------------------- 1 | namespace testing; 2 | 3 | import "std"; 4 | 5 | final unexported = 42; 6 | 7 | fun hey(message: str) > int { 8 | std\print(message); 9 | return unexported; 10 | } 11 | 12 | final ignore = "ignore me!"; 13 | 14 | object PrefixMe { 15 | name: str = "Joe", 16 | } 17 | 18 | export std\assert as assert; 19 | export hey; 20 | export ignore; 21 | export PrefixMe; 22 | --------------------------------------------------------------------------------