├── .gitattributes ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── TODO.md ├── build.sh ├── demos ├── advent22 │ ├── day1.nt │ ├── day10.nt │ ├── day11.nt │ ├── day12.nt │ ├── day13.nt │ ├── day14.nt │ ├── day15.nt │ ├── day16.nt │ ├── day17.nt │ ├── day18.nt │ ├── day19.nt │ ├── day2.nt │ ├── day20.nt │ ├── day21.nt │ ├── day22.nt │ ├── day23.nt │ ├── day24.nt │ ├── day25.nt │ ├── day3.nt │ ├── day4.nt │ ├── day5.nt │ ├── day6.nt │ ├── day7.nt │ ├── day8.nt │ └── day9.nt ├── alsatest.nt ├── breakout.nt ├── dragon.nt ├── fflame.nt ├── fib.nt ├── glfw.nt ├── gtkdemo │ ├── package-lock.json │ ├── package.json │ └── src │ │ └── main.nt ├── halma │ ├── .gitignore │ ├── package-lock.json │ ├── package.json │ └── src │ │ └── main.nt ├── hellogl.nt ├── life.nt ├── longestline.nt ├── quine.nt ├── raylib.nt ├── sdltest.nt ├── sha256sum.nt ├── shaders │ ├── frag.glsl │ ├── frag.spv │ ├── vert.glsl │ └── vert.spv ├── some_grass_or_we.png ├── spinny.nt └── vulkantest.nt ├── doc ├── compilerbase.md ├── generations.md ├── index.md ├── quarantine.md └── sphinx │ ├── _ext │ └── xe_quote.py │ ├── _static │ ├── breakelse │ │ ├── gurkenglas_idea.png │ │ ├── gurkenglas_looking.png │ │ ├── gurkenglas_neutral.png │ │ ├── gurkenglas_suggesting.png │ │ ├── gurkenglas_unimpressed.png │ │ ├── shoebill_aghast.png │ │ ├── shoebill_considering.png │ │ └── shoebill_neutral.png │ └── custom.css │ ├── breakelse.rst │ ├── conf.py │ ├── dconf_23.rst │ ├── getstarted.rst │ ├── index.rst │ ├── intro.rst │ └── manual.rst ├── docgen.sh ├── find-llvm-config.sh ├── neat.kdev4 ├── release-gcc.sh ├── release-llvm.sh ├── release-win64-gcc.sh ├── runtests.sh ├── src ├── backend │ ├── base.nt │ ├── c.nt │ └── llvm.nt ├── c │ ├── fcntl.nt │ ├── libgen.nt │ ├── pthread.nt │ ├── semaphore.nt │ ├── stdio.nt │ ├── stdlib.nt │ ├── sys │ │ ├── stat.nt │ │ └── time.nt │ ├── unistd.nt │ └── windows.nt ├── helpers.nt ├── main.nt ├── neat │ ├── FileIdTableImpl.nt │ ├── array.nt │ ├── base.nt │ ├── bottom.nt │ ├── class_.nt │ ├── compiler.nt │ ├── decl.nt │ ├── delegate_.nt │ ├── docgen.nt │ ├── either.nt │ ├── enums.nt │ ├── expr.nt │ ├── float.nt │ ├── formatstring.nt │ ├── function_.nt │ ├── hash.nt │ ├── hashmap.nt │ ├── heap_closures.nt │ ├── lambda.nt │ ├── lexer.nt │ ├── macrocache.nt │ ├── packages.nt │ ├── parser.nt │ ├── pragmas.nt │ ├── quasiquoting.nt │ ├── runtime.nt │ ├── runtime │ │ ├── array.nt │ │ ├── locrange.nt │ │ └── stdlib.nt │ ├── statements.nt │ ├── struct_.nt │ ├── stuff.nt │ ├── templ.nt │ ├── ternary.nt │ ├── traits.nt │ ├── tuples.nt │ ├── types.nt │ ├── union_.nt │ ├── unittest_.nt │ ├── util.nt │ ├── vectors.nt │ ├── with_.nt │ └── workpool.nt ├── polyhash.nt ├── runtime.c └── std │ ├── algorithm.nt │ ├── argparse.nt │ ├── container │ └── binheap.nt │ ├── error.nt │ ├── file.nt │ ├── http.nt │ ├── json.nt │ ├── json │ ├── macro.nt │ ├── stream.nt │ └── util.nt │ ├── macro │ ├── assert.nt │ ├── cimport.nt │ ├── easymacro.nt │ ├── hash.nt │ ├── listcomprehension.nt │ ├── once.nt │ ├── quasiquoting.nt │ └── the.nt │ ├── math.nt │ ├── math │ ├── matrix.nt │ └── vector.nt │ ├── process.nt │ ├── range │ └── iota.nt │ ├── sha256.nt │ ├── socket.nt │ ├── stdio.nt │ ├── stream.nt │ ├── string.nt │ ├── thread.nt │ └── time.nt ├── test ├── fail_compilation │ ├── abstract.nt │ ├── afl_1.nt │ ├── afl_2.nt │ ├── afl_3.nt │ ├── afl_4.nt │ ├── class_private_member.nt │ ├── class_private_method.nt │ ├── class_template.nt │ ├── contravariance.nt │ ├── covariance.nt │ ├── either.nt │ ├── import_private_member.nt │ ├── lambda_escape_class_field.nt │ ├── lambda_escape_return.nt │ ├── lambda_escape_return_either.nt │ ├── lambda_escape_return_tuple.nt │ ├── lambda_escape_struct_return.nt │ ├── long_literal.nt │ ├── missing_symbol_import.nt │ ├── mutable.nt │ ├── named_arg.nt │ ├── named_arg_class.nt │ ├── named_arg_struct.nt │ ├── named_arg_struct2.nt │ ├── nested_struct.nt │ ├── private_import_test.nt │ ├── silent_discard.nt │ ├── struct_private_member.nt │ └── struct_private_method.nt ├── imports │ ├── member.nt │ ├── private_import.nt │ ├── private_member.nt │ └── public_import.nt └── runnable │ ├── abstract.nt │ ├── ack.nt │ ├── algo.nt │ ├── aliastest.nt │ ├── arraytest.nt │ ├── autotest.nt │ ├── breakelse.nt │ ├── casts.nt │ ├── chartest.nt │ ├── classfntest.nt │ ├── classtest.nt │ ├── covariance_contravariance.nt │ ├── discard.nt │ ├── easymacro.nt │ ├── eithertest.nt │ ├── enumtest.nt │ ├── eqtest.nt │ ├── floattest.nt │ ├── forlooptest.nt │ ├── formatstrings.nt │ ├── funcptrtest.nt │ ├── hashmap.nt │ ├── heap_fun.nt │ ├── here.nt │ ├── hex.nt │ ├── iftest.nt │ ├── incdec.nt │ ├── intfs.nt │ ├── lambda_packing.nt │ ├── lambdas.nt │ ├── lifetime.nt │ ├── llvm.nt │ ├── macrotest.nt │ ├── malloctest.nt │ ├── mathtest.nt │ ├── module_level.nt │ ├── named_arg.nt │ ├── nested.nt │ ├── nested_import.nt │ ├── nestedfntest.nt │ ├── numbers.nt │ ├── obj_is.nt │ ├── opoverload.nt │ ├── order.nt │ ├── overloading.nt │ ├── parentest.nt │ ├── private_.nt │ ├── ptrarraytest.nt │ ├── ptrtest.nt │ ├── public_import_test.nt │ ├── quote_struct.nt │ ├── scientific.nt │ ├── sha256.nt │ ├── shortcircuittest.nt │ ├── stringtest.nt │ ├── structtest.nt │ ├── symbol_identifier.nt │ ├── templates.nt │ ├── ternary.nt │ ├── tuples.nt │ ├── typeof.nt │ ├── ufcs.nt │ ├── vartest.nt │ ├── vectortest.nt │ ├── versions.nt │ ├── weird.nt │ ├── whiletest.nt │ └── withtest.nt └── unittest.sh /.gitattributes: -------------------------------------------------------------------------------- 1 | *.nt linguist-language=D 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/.* 2 | build 3 | *.bc 4 | *.ll 5 | *.png 6 | *.o 7 | __pycache__ 8 | doc/sphinx/std.rst 9 | doc/sphinx/std/ 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neat-Lang/neat/7d39ee6abbd2f407656eab61a499e3cf9a9887b7/.gitmodules -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 2 | 3 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 4 | 5 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 6 | 7 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 8 | 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Neat 2 | 3 | Neat is a C-like/D1-like language with macros. This repo contains its compiler. 4 | 5 | To avoid duplication, please refer to the language documentation at . 6 | 7 | # In-repo documentation 8 | 9 | - [Compiler Internals](./doc/index.md) 10 | - [Testcases](./test/runnable/) 11 | - [Demo programs](./demos/) 12 | 13 | # License 14 | 15 | Neat is licensed under the [BSD 3-Clause license](./LICENSE). 16 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | blocking: 2 | - neat upgrade 3 | - neat --version 0.1.7 4 | - package.json "version", package-lock.json "version" 5 | - dedicated bool type 6 | 7 | pending: 8 | 9 | - ranges 10 | - fold ClassMethodPtr into LateSymbol 11 | - endLifetime should not take or need to take a Reference! 12 | - copyInto should not exist; instead there should be a copy() op that can then be chained into Assignment. 13 | - Sure? Needs more thinky. Maybe just `beginLifetime`? 14 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # kdevelop build mode 3 | if [ ! -z "${BUILD+x}" ] 4 | then 5 | FAST=1 6 | # no log spam 7 | set -euo pipefail 8 | else 9 | set -euxo pipefail 10 | fi 11 | OPTFLAG="-O -release" 12 | #OPTFLAG="" 13 | 14 | JFLAG="" 15 | if [ \! -z ${FAST+x} ] 16 | then 17 | OPTFLAG="" 18 | JFLAG="-j8" 19 | fi 20 | 21 | . ./find-llvm-config.sh 22 | 23 | FLAGS="$JFLAG -I$($LLVM_CONFIG --includedir) -L-L$($LLVM_CONFIG --libdir)" 24 | 25 | TAG=v0.7.1 26 | NEAT=.cache/bootstrap/"$TAG"/neat-"$TAG"-gcc/neat 27 | 28 | if [ ! -f "$NEAT" ] 29 | then 30 | echo "Downloading bootstrap compiler $TAG..." 31 | TARGET=.cache/bootstrap/"$TAG" 32 | rm -rf "$TARGET" 33 | mkdir -p "$TARGET" 34 | pushd "$TARGET" 35 | FILE=neat-"$TAG"-gcc.zip 36 | curl -L https://github.com/Neat-Lang/neat/releases/download/"$TAG"/"$FILE" --output "$FILE" 37 | unzip "$FILE" 38 | cd neat-"$TAG"-gcc 39 | echo "Building bootstrap compiler $TAG..." 40 | ./build.sh 41 | popd 42 | fi 43 | 44 | mkdir -p build 45 | 46 | echo "Building stage 1..." 47 | FLAGS="$FLAGS -version=LLVMBackend" 48 | FLAGS="$FLAGS -file-id-output build/fileIdPins" 49 | # see generation.md 50 | NEXT=compiler$(($($NEAT -print-generation) + 1)) 51 | $NEAT $FLAGS -backend=c -macro-backend=c -next-generation -P$NEXT:src -j src/main.nt \ 52 | -version=firstpass -o build/neat_stage1 53 | cat build/fileIdPins -< build/neat.ini 54 | -syspackage compiler:src 55 | -running-compiler-version=$TAG 56 | EOF 57 | rm build/fileIdPins 58 | NEAT=build/neat_stage1 59 | 60 | # store compiler source next to compiler 61 | rm -rf build/src 62 | cp -R src build/ 63 | 64 | echo "Building stage 2..." 65 | FLAGS="$FLAGS -version=LLVMBackend" 66 | # see generation.md 67 | $NEAT $FLAGS -backend=llvm -macro-backend=c -Pcompiler:src -j src/main.nt \ 68 | -o build/neat_stage2 69 | NEAT=build/neat_stage2 70 | 71 | echo "Building stage 3..." 72 | $NEAT $FLAGS $OPTFLAG -backend=llvm -macro-backend=llvm -Pcompiler:src -j src/main.nt \ 73 | -o build/neat_stage3 74 | NEAT=build/neat_stage3 75 | 76 | cp -f $NEAT build/neat 77 | rm build/neat_stage* 78 | 79 | cat build/fileIdPins -< build/neat.ini 80 | -syspackage compiler:src 81 | -running-compiler-version=$TAG 82 | EOF 83 | rm build/fileIdPins 84 | -------------------------------------------------------------------------------- /demos/advent22/day1.nt: -------------------------------------------------------------------------------- 1 | module day1; 2 | 3 | macro import std.macro.listcomprehension; 4 | 5 | import std.algorithm; 6 | import std.stdio; 7 | import std.string : atoi; 8 | 9 | void main() 10 | { 11 | auto lines = stdin.byLine.array; 12 | auto elves = lines.splitter(""); 13 | auto cals = elf => [sum cals.atoi for cals in elf]; 14 | auto first = [ 15 | argmax(elf.cals) (elf=(i+1), cals=elf.cals) 16 | for i, elf in elves]; 17 | 18 | print("Most calories carried by elf $(first.elf): $(first.cals)"); 19 | 20 | auto ordered = elves.map(cals).array.sort((a, b) => a > b); 21 | auto top3 = [sum a for a in ordered[0 .. 3]]; 22 | 23 | print("Top-3 total calories: $top3"); 24 | } 25 | -------------------------------------------------------------------------------- /demos/advent22/day10.nt: -------------------------------------------------------------------------------- 1 | module day10; 2 | 3 | macro import std.macro.listcomprehension; 4 | 5 | import std.algorithm; 6 | import std.math; 7 | import std.stdio; 8 | import std.string; 9 | 10 | alias Instr = (:noop | :addx, int i); 11 | 12 | Instr parse(string line) { 13 | auto line = line.split(" ").((cmd=that[0], args=that[1 .. $])); 14 | return line.cmd.case("noop": :noop, "addx": (:addx, line.args[0].atoi)); 15 | } 16 | 17 | class CPU { 18 | int x; 19 | int cycle; 20 | Instr[] pendingInstrs; 21 | (int | :none) pending; 22 | int interestingSignalStrength; 23 | string mut[] pixels; 24 | this(this.pendingInstrs) { 25 | cycle = 1; 26 | x = 1; 27 | pending = :none; 28 | pixels = [" " for i in 0 .. 40 * 6].dup; 29 | } 30 | void exec() { 31 | auto instr = pendingInstrs[0]; 32 | pendingInstrs = pendingInstrs[1 .. $]; 33 | instr.case { 34 | :noop: {} 35 | (:addx, int i): 36 | pending = x + i; 37 | } 38 | } 39 | void run() { 40 | while (true) { 41 | checkSignalStrength; 42 | pending.case { 43 | :none: 44 | if (pendingInstrs.empty) return; 45 | exec; 46 | int i: 47 | x = i; 48 | pending = :none; 49 | } 50 | checkPixel; 51 | cycle++; 52 | } 53 | } 54 | void checkSignalStrength() { 55 | if (cycle > 0 && (cycle - 20) % 40 == 0) { 56 | print("cycle $cycle, x $x, add $(x * cycle)"); 57 | interestingSignalStrength += x * cycle; 58 | } 59 | } 60 | void checkPixel() { 61 | if (abs(x - (cycle % 40)) < 2) { 62 | pixels[cycle] = "#"; 63 | } 64 | } 65 | } 66 | 67 | void main() 68 | { 69 | auto lines = stdin.byLine 70 | .filter(a => !a.empty) 71 | .map(a => parse(a)) 72 | .array; 73 | auto cpu = new CPU(lines); 74 | cpu.run; 75 | print("at cycle $(cpu.cycle): x=$(cpu.x), strength $(cpu.interestingSignalStrength)"); 76 | for (i in 0 .. 6) { 77 | print(cpu.pixels[i * 40 .. (i+1) * 40].dup.freeze.join("")); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /demos/advent22/day11.nt: -------------------------------------------------------------------------------- 1 | module day11; 2 | 3 | macro import std.macro.assert; 4 | macro import std.macro.listcomprehension; 5 | 6 | import std.algorithm; 7 | import std.math; 8 | import std.stdio; 9 | import std.string; 10 | 11 | alias Parameter = (:old | int); 12 | alias Op = (:mul | :add); 13 | alias Arith = (Parameter left, Op op, Parameter right); 14 | 15 | struct Monkey { 16 | int[] items; 17 | long inspected; 18 | Arith arith; 19 | int test, ifTrue, ifFalse; 20 | } 21 | 22 | long applyOp(Arith arith, int value) { 23 | long left = arith.left.case(int i: i, :old: value); 24 | long right = arith.right.case(int i: i, :old: value); 25 | return arith.op.case(:mul: left * right, :add: left + right); 26 | } 27 | 28 | Parameter parseParam(string s) { 29 | if (s == "old") return :old; 30 | return s.atoi; 31 | } 32 | 33 | Op parseOp(string s) => s.case("*": :mul, "+": :add); 34 | 35 | Monkey parseMonkey(string[] block) { 36 | assert(block[1].startsWith(" Starting items")); 37 | auto items = block[1].split(": ")[1] 38 | .split(", ").map(a => a.atoi).array; 39 | assert(block[2].startsWith(" Operation: new = ")); 40 | auto operation = block[2].split("new = ")[1] 41 | .split(" ") 42 | .(left=that[0].parseParam, op=that[1].parseOp, that[2].parseParam); 43 | assert(block[3].startsWith(" Test: divisible by ")); 44 | auto div = block[3].split(" by ")[1].atoi; 45 | auto ifTrue = block[4].split("throw to monkey ")[1].atoi; 46 | auto ifFalse = block[5].split("throw to monkey ")[1].atoi; 47 | return Monkey(items, 0, operation, div, ifTrue, ifFalse); 48 | } 49 | 50 | void round(Monkey mut[] monkeys, bool divideByThree, int divProd) { 51 | for (i in 0 .. monkeys.length) with (monkeys[i]) { 52 | for (item in items) { 53 | inspected++; 54 | auto newWorryLevel = arith.applyOp(item) / 3 if divideByThree else arith.applyOp(item); 55 | auto managedWorryLevel = cast(int) (newWorryLevel % divProd); 56 | if (managedWorryLevel % test == 0) 57 | monkeys[ifTrue].items ~= managedWorryLevel; 58 | else 59 | monkeys[ifFalse].items ~= managedWorryLevel; 60 | } 61 | items = null; 62 | } 63 | } 64 | 65 | void main() 66 | { 67 | auto lines = stdin.byLine.array; 68 | auto monkeys = lines.splitter("").map(a => a.array.parseMonkey).array; 69 | // "keep worry levels manageable" so operate in ring appropriate for divisor checks 70 | mut int divProd = 1; 71 | for (monkey in monkeys) divProd *= monkey.test; 72 | 73 | for (rounds in [20, 10000]) { 74 | auto monkeys2 = monkeys.dup; 75 | for (_ in 0 .. rounds) { 76 | round(monkeys2, divideByThree=(true if rounds == 20 else false), divProd=divProd); 77 | } 78 | auto monkeys2 = monkeys2.freeze.sort((a, b) => a.inspected > b.inspected); 79 | auto monkeyBusiness = monkeys2.(that[0].inspected * that[1].inspected); 80 | print("after $rounds rounds, monkey business $monkeyBusiness"); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /demos/advent22/day12.nt: -------------------------------------------------------------------------------- 1 | module day12; 2 | 3 | macro import std.macro.assert; 4 | macro import std.macro.listcomprehension; 5 | 6 | import std.algorithm; 7 | import std.math; 8 | import std.stdio; 9 | import std.string; 10 | 11 | void main() 12 | { 13 | auto lines = stdin.byLine 14 | .filter(a => !a.empty) 15 | .array; 16 | auto startY = [first i for i, line in lines where line.any(ch => ch == 'S')]; 17 | auto startX = [first i for i, ch in lines[startY] where ch == 'S']; 18 | auto endY = [first i for i, line in lines where line.any(ch => ch == 'E')]; 19 | auto endX = [first i for i, ch in lines[endY] where ch == 'E']; 20 | auto start = vec2l(startX, startY); 21 | auto end = vec2l(endX, endY); 22 | auto heights = lines 23 | .map(line => line.map(ch => ch.height).array) 24 | .array; 25 | auto size = vec2l(heights[0].length, heights.length); 26 | auto bestScore = [[-1 for i in 0 .. size.x].dup for j in 0 .. size.y].dup; 27 | bestScore[end.y][end.x] = 0; 28 | mut vec2l[] checkQueue = [end]; 29 | while (!checkQueue.empty) { 30 | auto to = checkQueue[$ - 1]; 31 | checkQueue = checkQueue[0 .. $ - 1]; 32 | auto myScore = bestScore[to.y][to.x]; 33 | assert(myScore != -1); 34 | bool reachableFrom(vec2l from) { 35 | return from.(x >= 0 && y >= 0 && x < size.x && y < size.y 36 | && heights[to.y][to.x] <= heights[y][x] + 1); 37 | } 38 | void check(vec2l d) { 39 | auto from = to + d; 40 | if (reachableFrom(from) && bestScore[from.y][from.x].(that == -1 || myScore + 1 < that)) { 41 | bestScore[from.y][from.x] = myScore + 1; 42 | checkQueue ~= from; 43 | } 44 | } 45 | check(vec2l(0, -1)); 46 | check(vec2l(0, 1)); 47 | check(vec2l(-1, 0)); 48 | check(vec2l(1, 0)); 49 | } 50 | print("shortest path length: $(bestScore[start.y][start.x])"); 51 | auto locations = [join [vec2l(x, y) for x in 0 .. size.x] for y in 0 .. size.y]; 52 | auto aSquares = [start] ~ [l for l in locations where heights[l.y][l.x] == 0]; 53 | auto shortestFromA = [min bestScore[a.y][a.x] for a in aSquares where bestScore[a.y][a.x] != -1]; 54 | print("shortest path from any a square: $(shortestFromA)"); 55 | } 56 | 57 | alias vec2l = Vector(size_t, 2); 58 | 59 | int height(char ch) => 0 if ch == 'S' else 25 if ch == 'E' else ch - 'a'; 60 | -------------------------------------------------------------------------------- /demos/advent22/day13.nt: -------------------------------------------------------------------------------- 1 | module day13; 2 | 3 | macro import std.macro.assert; 4 | macro import std.macro.listcomprehension; 5 | 6 | import std.algorithm; 7 | import std.math; 8 | import std.stdio; 9 | import std.string; 10 | 11 | void main() { 12 | auto lines = stdin.byLine.array; 13 | auto pairs = lines.splitter("") 14 | .map(pair => pair.array.(first=that[0].parse, second=that[1].parse)) 15 | .array; 16 | auto rightIndices = [i + 1 for i, pair in pairs where compare(pair.first, pair.second) == :right]; 17 | print("right indices $rightIndices, sum $([sum a for a in rightIndices])"); 18 | auto dividers = [Value([Value([Value(2)])]), Value([Value([Value(6)])])]; 19 | auto allLines = lines.filter(a => !a.empty).map(a => a.parse).array ~ dividers; 20 | auto smaller = cmpResult => cmpResult.case(:wrong: false, :right: true, :equal: false); 21 | auto sorted = allLines.sort((a, b) => compare(a, b).smaller); 22 | auto div1 = [first i for i, a in sorted where a == dividers[0]] + 1; 23 | auto div2 = [first i for i, a in sorted where a == dividers[1]] + 1; 24 | print("divider = $div1, divider 2 $div2, key $(div1 * div2)"); 25 | } 26 | 27 | alias CmpResult = (:wrong | :right | :equal); 28 | 29 | CmpResult compare(Value left, Value right) { 30 | left.value.case { 31 | int leftInt: right.value.case { 32 | int rightInt: 33 | if (leftInt < rightInt) return :right; 34 | if (leftInt > rightInt) return :wrong; 35 | return :equal; 36 | Value[] j: return compare(Value([left]), right); 37 | } 38 | Value[] i: right.value.case { 39 | int j: return compare(left, Value([right])); 40 | Value[] j: return compare(i, j); 41 | } 42 | } 43 | } 44 | 45 | CmpResult compare(Value[] left, Value[] right) { 46 | for (i in 0 .. max(left.length, right.length)) { 47 | if (i >= left.length) return :right; 48 | if (i >= right.length) return :wrong; 49 | auto cmpElement = compare(left[i], right[i]); 50 | if (cmpElement != :equal) return cmpElement; 51 | } 52 | return :equal; 53 | } 54 | 55 | struct Value { 56 | (Value[] | int) value; 57 | string toString() => value.case(Value[] e: "$e", int i: "$i"); 58 | } 59 | 60 | Value parse(string str) => (new Parser(str)).parse; 61 | 62 | class Parser { 63 | string str; 64 | this(this.str) {} 65 | Value parse() { 66 | if (str.front == '[') { 67 | str = str[1 .. $]; 68 | mut Value[] ret; 69 | while (str.front != ']') { 70 | ret ~= parse; 71 | if (str.front == ',') str = str[1 .. $]; 72 | } 73 | str = str[1 .. $]; 74 | return Value(ret); 75 | } 76 | auto next = str.until(",").until("]"); 77 | str = str[next.length .. $]; 78 | return Value(next.atoi); 79 | } 80 | } 81 | 82 | string until(string haystack, string needle) { 83 | auto pos = haystack.find(needle); 84 | if (pos == -1) return haystack; 85 | return haystack[0 .. pos]; 86 | } 87 | -------------------------------------------------------------------------------- /demos/advent22/day14.nt: -------------------------------------------------------------------------------- 1 | module day14; 2 | 3 | macro import std.macro.assert; 4 | macro import std.macro.listcomprehension; 5 | 6 | import std.algorithm; 7 | import std.math; 8 | import std.stdio; 9 | import std.string; 10 | 11 | void main() 12 | { 13 | auto lines = stdin.byLine 14 | .filter(a => !a.empty) 15 | .map(line => decodePath(line)) 16 | .array; 17 | auto sandSim = new SandSim; 18 | for (line in lines) { 19 | for (int i in 0 .. cast(int) line.length - 1) { 20 | auto from = line[i], to = line[i + 1]; 21 | sandSim.drawLine(from, to); 22 | } 23 | } 24 | 25 | { 26 | auto task1 = sandSim.dup; 27 | task1.simulate; 28 | auto restingSand = [count a in task1.cave.values where a == :sand]; 29 | print("task 1: sand that comes to rest: $restingSand"); 30 | } 31 | { 32 | auto task2 = sandSim.dup; 33 | task2.simulate(floor=task2.yLimit + 2); 34 | auto restingSand = [count a in task2.cave.values where a == :sand]; 35 | print("task 2: sand that comes to rest: $restingSand"); 36 | } 37 | } 38 | 39 | class SandSim { 40 | Tile[vec2i] cave; 41 | int yLimit; 42 | this() {} 43 | this(this.cave, this.yLimit) {} 44 | void drawLine(vec2i from, vec2i to) { 45 | auto abs1 = i => 1 if i == 0 else i.abs; 46 | auto step = (to - from).(vec2i(x / abs1(x), y / abs1(y))); 47 | assert(step.(max(x.abs, y.abs) == 1 && min(x.abs, y.abs) == 0)); 48 | auto steps = (to - from).(x / step.x if step.x != 0 else y / step.y); 49 | print("$(from.(x, y)), $(to.(x, y)): $(step.(x, y)); $steps"); 50 | for (int j in 0 .. steps + 1) { 51 | auto linePos = from + step * j; 52 | cave[linePos] = :rock; 53 | yLimit = max(yLimit, linePos.y); 54 | } 55 | } 56 | SandSim dup() { 57 | mut Tile[vec2i] caveDup; 58 | for (key in cave.keys) caveDup[key] = cave[key]; 59 | return new SandSim(caveDup, this.yLimit); 60 | } 61 | void simulate(int floor = -1) { 62 | if (floor != -1) print("artificial floor at y = $floor"); 63 | vec2i spawn = vec2i(500, 0); 64 | // TODO break outer 65 | mut bool done = false; 66 | while (!done && cave.get(spawn, :air) != :sand) { 67 | mut vec2i pos = spawn; 68 | while (true) { 69 | bool check(vec2i step) { 70 | auto newPos = pos + step; 71 | if (floor != -1 && newPos.y == floor) return false; 72 | if (cave.get(newPos, :air) == :air) { 73 | pos = newPos; 74 | return true; 75 | } 76 | return false; 77 | } 78 | check(vec2i(0, 1)) || check(vec2i(-1, 1)) || check(vec2i(1, 1)) || break; 79 | if (floor == -1 && pos.y > yLimit) { 80 | done = true; 81 | break; 82 | } 83 | } 84 | if (!done) { 85 | cave[pos] = :sand; 86 | } 87 | } 88 | } 89 | } 90 | 91 | alias Tile = (:air | :rock | :sand); 92 | 93 | alias vec2i = Vector(int, 2); 94 | 95 | vec2i[] decodePath(string line) { 96 | return line.split(" -> ") 97 | .map(pair => pair.split(",").(vec2i(that[0].atoi, that[1].atoi))) 98 | .array; 99 | } 100 | -------------------------------------------------------------------------------- /demos/advent22/day18.nt: -------------------------------------------------------------------------------- 1 | module day18; 2 | 3 | import std.algorithm; 4 | import std.stdio; 5 | import std.string; 6 | 7 | void main() 8 | { 9 | auto decodeVector = line => line 10 | .split(",") 11 | .(V(that[0].atoi, that[1].atoi, that[2].atoi)); 12 | auto cubes = stdin.byLine 13 | .filter(a => !a.empty) 14 | .map(decodeVector) 15 | .array; 16 | auto adjacentSides = [ 17 | V(1, 0, 0), 18 | V(-1, 0, 0), 19 | V(0, 1, 0), 20 | V(0, -1, 0), 21 | V(0, 0, 1), 22 | V(0, 0, -1), 23 | ]; 24 | alias PathId = int; 25 | mut (:unknown | :solid | :explored, PathId)[V] material; 26 | for (cube in cubes) material[cube] = :solid; 27 | mut int surfaceExposed = 0; 28 | mut auto boundingBox = (from=V(1), to=V(-1)); 29 | for (cube in cubes) { 30 | boundingBox.from = min(boundingBox.from, cube); 31 | boundingBox.to = max(boundingBox.to, cube); 32 | } 33 | for (cube in cubes) { 34 | for (adjacentSide in adjacentSides) { 35 | auto mat = material.get(cube + adjacentSide, :unknown); 36 | surfaceExposed += 0 if mat == :solid else 1; 37 | } 38 | } 39 | print("exposed sides: $surfaceExposed"); 40 | mut (:air | :solid)[PathId] outcome; 41 | bool canPathToOutside(V pos, PathId pathId) { 42 | if (boundingBox.from.(pos.x < x || pos.y < y || pos.z < z) 43 | || boundingBox.to.(pos.x > x || pos.y > y || pos.z > z)) 44 | return true; 45 | material.get(pos, :unknown).case { 46 | :unknown: 47 | material[pos] = (:explored, pathId); 48 | for (adjacentSide in adjacentSides) { 49 | if (canPathToOutside(pos + adjacentSide, pathId)) 50 | return true; 51 | } 52 | :solid: {} 53 | (:explored, PathId otherId): 54 | if (otherId == pathId) return false; 55 | return outcome[otherId] == :air; 56 | } 57 | return false; 58 | } 59 | mut int surfaceExposedToAir = 0; 60 | mut PathId pathCounter = 0; 61 | for (cube in cubes) { 62 | for (adjacentSide in adjacentSides) { 63 | if (canPathToOutside(cube + adjacentSide, pathId=pathCounter)) { 64 | outcome[pathCounter++] = :air; 65 | surfaceExposedToAir ++; 66 | } 67 | else { 68 | outcome[pathCounter++] = :solid; 69 | } 70 | } 71 | } 72 | print("surface exposed to actual air: $surfaceExposedToAir"); 73 | } 74 | 75 | V max(V a, V b) => V(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z)); 76 | V min(V a, V b) => V(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z)); 77 | int max(int a, int b) => a if a > b else b; 78 | int min(int a, int b) => a if a < b else b; 79 | 80 | alias V = Vector(int, 3); 81 | -------------------------------------------------------------------------------- /demos/advent22/day2.nt: -------------------------------------------------------------------------------- 1 | module day2; 2 | 3 | macro import std.macro.assert; 4 | macro import std.macro.listcomprehension; 5 | 6 | import std.algorithm; 7 | import std.stdio; 8 | import std.string; 9 | 10 | enum Shape { rock, paper, scissors } 11 | enum Outcome { win, loss, draw } 12 | alias Game = (Shape theirs, Shape mine); 13 | 14 | Game parseInterp1(string theirs, string mine) => ( 15 | Shape.(theirs.case("A": rock, "B": paper, "C": scissors)), 16 | Shape.(mine.case("X": rock, "Y": paper, "Z": scissors))); 17 | 18 | (Shape theirs, Outcome mine) parseInterp2(string theirs, string mine) => ( 19 | Shape.(theirs.case("A": rock, "B": paper, "C": scissors)), 20 | Outcome.(mine.case("X": loss, "Y": draw, "Z": win))); 21 | 22 | Game resolve((Shape theirs, Outcome required) strategy) => strategy.(( 23 | theirs=theirs, 24 | ours=[ 25 | first mine for mine in Shape.([rock, paper, scissors]) 26 | where outcome((theirs, mine)) == required])); 27 | 28 | Outcome outcome(Game game) with (Outcome) { 29 | bool beats(Shape mine, Shape theirs) return Shape.( 30 | mine == rock && theirs == scissors 31 | || mine == paper && theirs == rock 32 | || mine == scissors && theirs == paper); 33 | if (game.mine == game.theirs) return draw; 34 | if (game.mine.beats(game.theirs)) return win; 35 | return loss; 36 | } 37 | 38 | int judgeGame(Game game) => Outcome.(game.outcome.case(win: 6, draw: 3, loss: 0)); 39 | 40 | int judgeShape(Shape shape) => Shape.(shape.case(rock: 1, paper: 2, scissors: 3)); 41 | 42 | void main() 43 | { 44 | auto input = stdin.byLine 45 | .filter(a => !a.empty) 46 | .map(a => a.split(" ").((theirs=that[0], mine=that[1]))) 47 | .array; 48 | 49 | auto strategy1 = input.map(a => parseInterp1(theirs=a.theirs, mine=a.mine)).array; 50 | auto score1 = [sum (a.judgeGame + a.mine.judgeShape) for a in strategy1]; 51 | print("Strategy guide score, interpretation 1 is $score1"); 52 | 53 | auto strategy2 = input.map(a => parseInterp2(theirs=a.theirs, mine=a.mine).resolve).array; 54 | auto score2 = [sum (a.judgeGame + a.mine.judgeShape) for a in strategy2]; 55 | print("Strategy guide score, interpretation 2 is $score2"); 56 | } 57 | -------------------------------------------------------------------------------- /demos/advent22/day20.nt: -------------------------------------------------------------------------------- 1 | module day20; 2 | 3 | macro import std.macro.assert; 4 | macro import std.macro.listcomprehension; 5 | 6 | import std.algorithm; 7 | import std.math; 8 | import std.stdio; 9 | import std.string; 10 | 11 | void main() 12 | { 13 | auto lines = stdin.byLine 14 | .filter(a => !a.empty) 15 | .map(a => a.atoi) 16 | .array; 17 | alias Sequence = (int initialIndex, long value) mut[]; 18 | void process(Sequence sequence, int rounds) { 19 | void swap(size_t a, size_t b) { 20 | auto v = sequence[a % $]; 21 | // FIXME array index context for assignment 22 | sequence[a % sequence.length] = sequence[b % $]; 23 | sequence[b % sequence.length] = v; 24 | } 25 | void swapForward(size_t pos, long distance) { 26 | for (x in 0 .. distance % (sequence.length - 1)) { 27 | swap(pos + x, pos + x + 1); 28 | } 29 | } 30 | void swapBackward(size_t pos, long distance) { 31 | for (x in 0 .. distance % (sequence.length - 1)) { 32 | swap(pos - x, pos - x - 1); 33 | } 34 | } 35 | for (round in 0 .. rounds) { 36 | for (i in 0 .. sequence.length) { 37 | auto entry = [first (pos=j, value=a.value) for j, a in sequence 38 | where a.initialIndex == i]; 39 | if (entry.value > 0) 40 | swapForward(entry.pos, entry.value); 41 | else 42 | swapBackward(entry.pos, -entry.value); 43 | // print("$i: moved $(entry.value): $([a.value for a in sequence])"); 44 | } 45 | } 46 | } 47 | long numAfter0(Sequence sequence, size_t offs) { 48 | mut auto pos = [first i for i, a in sequence where a.value == 0]; 49 | for (_ in 0 .. offs) pos = (pos + 1) % sequence.length; 50 | return sequence[pos].value; 51 | } 52 | void report(Sequence sequence, string prefix) { 53 | auto res1 = sequence.numAfter0(1000); 54 | auto res2 = sequence.numAfter0(2000); 55 | auto res3 = sequence.numAfter0(3000); 56 | print("$prefix: $res1 + $res2 + $res3 == $(res1 + res2 + res3)"); 57 | } 58 | Sequence sequence1 = [(initialIndex=cast(int) i, value=cast(long) a) for i, a in lines].dup; 59 | sequence1.process(rounds=1); 60 | sequence1.report("First half"); 61 | Sequence sequence2 = [ 62 | (initialIndex=cast(int) i, value=cast(long) a * 811589153) for i, a in lines].dup; 63 | sequence2.process(rounds=10); 64 | sequence2.report("Second half"); 65 | } 66 | -------------------------------------------------------------------------------- /demos/advent22/day23.nt: -------------------------------------------------------------------------------- 1 | module day23; 2 | 3 | macro import std.macro.assert; 4 | macro import std.macro.listcomprehension; 5 | 6 | import std.algorithm; 7 | import std.stdio; 8 | 9 | void main() 10 | { 11 | auto lines = stdin.byLine 12 | .filter(line => !line.empty) 13 | .array; 14 | mut V[] elves; 15 | for (int y, line in lines) 16 | for (int x, ch in line) 17 | if (ch == '#') 18 | elves ~= V(x, y); 19 | V[] checks = [V(0, -1), V(0, 1), V(-1, 0), V(1, 0)]; 20 | print("elves: $elves"); 21 | (V[] state, bool anyMoved) step(V[] elves, int round) { 22 | mut int[V] proposals; 23 | bool free(V v) => [all a != v for a in elves]; 24 | bool freeAround(V v, V checkDir) { 25 | auto checkL = checkDir.(V(x if x else 1, y if y else 1)); 26 | auto checkR = checkDir.(V(x if x else -1, y if y else -1)); 27 | return free(v + checkDir) && free(v + checkL) && free(v + checkR); 28 | } 29 | bool mustMove(V v) { 30 | for (y in -1 .. 2) for (x in -1 .. 2) if (x || y) 31 | if (!free(v + V(x, y))) return true; 32 | return false; 33 | } 34 | for (elf in elves) { 35 | if (!elf.mustMove) continue; 36 | for (i in 0 .. 4) { 37 | auto checkDir = checks[(i + round) % 4]; 38 | if (freeAround(elf, checkDir)) { 39 | auto key = elf + checkDir; 40 | // print("elf at $elf proposes moving $(checkDir)"); 41 | proposals[key] = proposals.get(key, 0) + 1; 42 | break; 43 | } 44 | } 45 | } 46 | mut V[] newElves; 47 | mut bool anyMoved; 48 | for (elf in elves) { 49 | mut bool moved = false; 50 | if (elf.mustMove) { 51 | for (i in 0 .. 4) { 52 | auto checkDir = checks[(i + round) % 4]; 53 | if (freeAround(elf, checkDir)) { 54 | if (proposals[elf + checkDir] == 1) { 55 | // print("elf at $elf moves $(checkDir)"); 56 | newElves ~= elf + checkDir; 57 | moved = true; 58 | anyMoved = true; 59 | } 60 | break; 61 | } 62 | } 63 | } 64 | if (!moved) 65 | newElves ~= elf; 66 | } 67 | // print("after step $round: $newElves"); 68 | return (newElves, anyMoved); 69 | } 70 | int free(V[] state) { 71 | mut int free; 72 | auto bb = ( 73 | from=V([min(v.x) for v in state], [min(v.y) for v in state]), 74 | to=V([max(v.x) for v in state], [max(v.y) for v in state])); 75 | for (y in bb.from.y .. bb.to.y + 1) 76 | for (x in bb.from.x .. bb.to.x + 1) 77 | if (![any a == V(x, y) for a in state]) 78 | free++; 79 | return free; 80 | } 81 | mut V[] state = elves; 82 | for (i in 0 .. 10) state = step(state, i).state; 83 | print("After round 10: $(state.free) free"); 84 | for (i in 10 .. int.max) { 85 | auto res = step(state, i); 86 | if (!res.anyMoved) { 87 | print("in round $(i + 1), no elves moved."); 88 | break; 89 | } 90 | state = res.state; 91 | } 92 | } 93 | 94 | alias V = Vector(int, 2); 95 | -------------------------------------------------------------------------------- /demos/advent22/day25.nt: -------------------------------------------------------------------------------- 1 | module day25; 2 | 3 | macro import std.macro.assert; 4 | macro import std.macro.listcomprehension; 5 | 6 | import std.algorithm; 7 | import std.stdio; 8 | 9 | void main() 10 | { 11 | auto sum = [sum a for a in stdin.byLine.map(a => decodeSnafu(a.dup))]; 12 | print("sum is $sum, snafu $(sum.encodeSnafu)"); 13 | } 14 | 15 | long decodeSnafu(char mut[] s) { 16 | mut long factor = 1; 17 | mut long res; 18 | for (i in 0 .. s.length) { 19 | res += factor * s[$ - 1 - i].case( 20 | '0': 0, 21 | '1': 1, 22 | '2': 2, 23 | '-': -1, 24 | '=': -2); 25 | factor *= 5; 26 | } 27 | return res; 28 | } 29 | 30 | string encodeSnafu(long l) { 31 | mut int digits = 1; 32 | while (l > decodeSnafu(['2' for i in 0 .. digits].dup) 33 | || l < decodeSnafu(['=' for i in 0 .. digits].dup)) { 34 | digits ++; 35 | } 36 | mut char mut[] upper = ['2' for i in 0 .. digits].dup, 37 | lower = ['=' for i in 0 .. digits].dup; 38 | for (digit in 0 .. digits) { 39 | bool test(char ch) { 40 | lower[digit] = ch; 41 | upper[digit] = ch; 42 | return l >= lower.decodeSnafu && l <= upper.decodeSnafu; 43 | } 44 | bottom die() assert(false); 45 | test('=') || test('-') || test('0') || test('1') || test('2') || die; 46 | } 47 | auto lower = lower.freeze, upper = upper.freeze; 48 | assert(lower == upper); 49 | return lower; 50 | } 51 | -------------------------------------------------------------------------------- /demos/advent22/day3.nt: -------------------------------------------------------------------------------- 1 | module day3; 2 | 3 | macro import std.macro.assert; 4 | macro import std.macro.listcomprehension; 5 | 6 | import std.algorithm; 7 | import std.stdio; 8 | import std.string; 9 | 10 | int priority(char ch) { 11 | if (ch >= 'a' && ch <= 'z') return 1 + ch - 'a'; 12 | if (ch >= 'A' && ch <= 'Z') return 27 + ch - 'A'; 13 | assert(false); 14 | } 15 | 16 | T[] overlap(T)(T[] first, T[] second) { 17 | return first.filter(a => [any a == b for b in second]).array; 18 | } 19 | 20 | T[] overlap3(T)(T[] first, T[] second, T[] third) { 21 | return first.overlap(second).overlap(third); 22 | } 23 | 24 | void main() 25 | { 26 | auto lines = stdin.byLine.filter(a => !a.empty).array; 27 | auto result1 = lines 28 | .map(a => (left=a[0..$/2], right=a[$/2..$])) 29 | .map(pair => pair.(overlap(left, right))[0]) 30 | .([sum ch.priority for ch in that]); 31 | print("Sum of priorities is $result1"); 32 | auto result2 = lines 33 | .([that[i*3 .. (i+1)*3] for i in 0 .. that.length / 3]) 34 | .map(group => overlap3(group[0], group[1], group[2])[0]) 35 | .([sum ch.priority for ch in that]); 36 | print("Sum of badge priorities is $result2"); 37 | } 38 | -------------------------------------------------------------------------------- /demos/advent22/day4.nt: -------------------------------------------------------------------------------- 1 | module day4; 2 | 3 | macro import std.macro.assert; 4 | macro import std.macro.listcomprehension; 5 | 6 | import std.algorithm; 7 | import std.math; 8 | import std.stdio; 9 | import std.string; 10 | 11 | void main() 12 | { 13 | auto parseRange = range => range.split("-").((from=that[0].atoi, to=that[1].atoi)); 14 | auto assignments = stdin.byLine 15 | .filter(a => !a.empty) 16 | .map(line => line.split(",").((first=that[0].parseRange, second=that[1].parseRange))) 17 | .array; 18 | auto overlap = (a, b) => (from=max(a.from, b.from), to=(min(a.to, b.to))); 19 | 20 | auto fullyContained = (a, b) => a.overlap(b).(that == a || that == b); 21 | auto result1 = [count a in assignments where fullyContained(a.first, a.second)]; 22 | print("Assignments pairs where one range fully contains the other: $result1"); 23 | 24 | auto overlapAtAll = (a, b) => a.overlap(b).(that.from <= that.to); 25 | auto result2 = [count a in assignments where overlapAtAll(a.first, a.second)]; 26 | print("Assignments pairs where the ranges overlap at all: $result2"); 27 | } 28 | -------------------------------------------------------------------------------- /demos/advent22/day5.nt: -------------------------------------------------------------------------------- 1 | module day5; 2 | 3 | macro import std.macro.assert; 4 | macro import std.macro.listcomprehension; 5 | 6 | import std.algorithm; 7 | import std.math; 8 | import std.stdio; 9 | import std.string; 10 | 11 | void main() 12 | { 13 | auto lines = stdin.byLine.array; 14 | auto uncrate = word => word[1 .. 2]; 15 | auto emptyLine = [first i for i, line in lines where line == ""]; 16 | auto crates = lines[0 .. emptyLine - 1]; 17 | auto crates = [[line[i*4 .. i*4+3].uncrate for i in 0 .. (line.length + 1) / 4] for line in crates]; 18 | print("crates = $crates"); 19 | mut auto crates = crates.rtranspose; 20 | auto moves = lines[emptyLine + 1 .. $] 21 | .map(move => move.split(" ").((count=that[1].atoi, from=that[3].atoi - 1, to=that[5].atoi - 1))) 22 | .array; 23 | 24 | auto crates1 = [line.dup for line in crates].dup; 25 | void move1(int count, int from, int to) { 26 | for (i in 0 .. count) { 27 | auto entry = crates1[from][$ - 1]; 28 | crates1[from] = crates1[from][0 .. $ - 1]; 29 | crates1[to] ~= entry; 30 | } 31 | } 32 | [move1(a.count, a.from, a.to) for a in moves]; 33 | auto tops1 = [join a[$ - 1] for a in crates1]; 34 | print("crates on top: $tops1"); 35 | 36 | auto crates2 = [line.dup for line in crates].dup; 37 | void move2(int count, int from, int to) { 38 | auto stack = crates2[from][$ - count .. $]; 39 | crates2[from] = crates2[from][0 .. $ - count]; 40 | crates2[to] ~= stack; 41 | } 42 | [move2(a.count, a.from, a.to) for a in moves]; 43 | auto tops2 = [join a[$ - 1] for a in crates2]; 44 | print("crates on top: $tops2"); 45 | } 46 | 47 | string[][] rtranspose(string[][] crates) { 48 | auto width = [max a.length for a in crates]; 49 | mut string[] mut[] result = new string[] mut[](width); 50 | for (mut int i = cast(int) crates.length - 1; i >= 0; i--) { 51 | for (k, crate in crates[i]) if (!crate.strip.empty) { 52 | result[k] ~= crate; 53 | } 54 | } 55 | return result.freeze; 56 | } 57 | -------------------------------------------------------------------------------- /demos/advent22/day6.nt: -------------------------------------------------------------------------------- 1 | module day6; 2 | 3 | macro import std.macro.assert; 4 | macro import std.macro.listcomprehension; 5 | 6 | import std.algorithm; 7 | import std.math; 8 | import std.stdio; 9 | import std.string; 10 | 11 | void main() 12 | { 13 | auto line = stdin.byLine.array[0]; 14 | auto allUniq = str => [all [all ch != ch2 || i == i2 for i2, ch2 in str] for i, ch in str]; 15 | auto result = width => 16 | [first i for i in 0 .. line.length - width where line[i .. i + width].allUniq] + width; 17 | print("Start of packet $(result(4))"); 18 | print("Start of message $(result(14))"); 19 | } 20 | -------------------------------------------------------------------------------- /demos/advent22/day7.nt: -------------------------------------------------------------------------------- 1 | module day7; 2 | 3 | macro import std.macro.assert; 4 | macro import std.macro.listcomprehension; 5 | 6 | import std.algorithm; 7 | import std.stdio; 8 | import std.string; 9 | 10 | alias Entry = (Folder, string | int, string); 11 | 12 | class Folder { 13 | Entry[] entries; 14 | this() { } 15 | Folder cd(string[] path) { 16 | if (path.empty) return this; 17 | for (entry in entries) entry.case { 18 | (Folder fdr, string name): 19 | if (name == path[0]) return fdr.cd(path[1 .. $]); 20 | (int size, string name): 21 | if (name == path[0]) { 22 | print("$(path[0]) is not a directory!"); 23 | assert(false); 24 | } 25 | } 26 | print("no such path: $path"); 27 | assert(false); 28 | } 29 | } 30 | 31 | class Computer { 32 | string[] cwd; 33 | Folder root; 34 | this() { this.root = new Folder; } 35 | void goToFolder(string folder) { 36 | if (folder == "/") cwd = []; 37 | else if (folder == "..") cwd = cwd[0 .. $ - 1]; 38 | else cwd ~= folder; 39 | } 40 | void addFiles(string[] args) { 41 | auto folder = root.cd(cwd); 42 | for (arg in args) { 43 | if (arg.startsWith("dir ")) { 44 | folder.entries ~= (new Folder, arg[4 .. $]); 45 | } else { 46 | auto parts = arg.split(" "); 47 | folder.entries ~= (parts[0].atoi, parts[1]); 48 | } 49 | } 50 | } 51 | void exec(string cmd, string[] args) { 52 | if (cmd.startsWith("cd ")) 53 | goToFolder(cmd[3 .. $]); 54 | else if (cmd.startsWith("ls")) 55 | addFiles(args); 56 | else { 57 | print("unknown command: $cmd $args"); 58 | assert(false); 59 | } 60 | } 61 | } 62 | 63 | void solve(Computer computer) { 64 | Folder[] allFolders() { 65 | Folder[] subfolders(Folder f) { 66 | mut Folder[] res; 67 | for (entry in f.entries) entry.case { 68 | (Folder g, string): res ~= g; 69 | (int sz, string): {} 70 | } 71 | return res; 72 | } 73 | Folder[] walk(Folder f) { 74 | return [f] ~ [join walk(g) for g in f.subfolders]; 75 | } 76 | return walk(computer.root); 77 | } 78 | int size(Folder folder) { 79 | int walk(Entry e) { 80 | return e.case( 81 | (Folder f, string): size(f), 82 | (int size, string): size); 83 | } 84 | return [sum walk(e) for e in folder.entries]; 85 | } 86 | auto result = [sum f.size for f in allFolders where f.size < 100000]; 87 | print("Sum of total sizes: $result"); 88 | int disksz = 70_000_000; 89 | int targetUsed = 40_000_000; 90 | int used = computer.root.size; 91 | int needToFree = used - targetUsed; 92 | auto result2 = [argmin(f.size) f.size for f in allFolders where f.size >= needToFree]; 93 | print("The total size is $result2"); 94 | } 95 | 96 | void main() 97 | { 98 | auto lines = stdin.byLine.array; 99 | auto computer = new Computer; 100 | for (mut int i = 0; i < lines.length; i++) { 101 | auto cmd = lines[i][2 .. $]; 102 | mut string[] args; 103 | while (i + 1 < lines.length && !lines[i + 1].startsWith("\$")) { 104 | i++; 105 | args ~= lines[i]; 106 | } 107 | computer.exec(cmd, args); 108 | } 109 | solve(computer); 110 | } 111 | -------------------------------------------------------------------------------- /demos/advent22/day8.nt: -------------------------------------------------------------------------------- 1 | module day8; 2 | 3 | macro import std.macro.listcomprehension; 4 | 5 | import std.algorithm; 6 | import std.stdio; 7 | import std.string; 8 | 9 | void main() 10 | { 11 | auto lines = stdin.byLine.filter(a => !a.empty).array; 12 | auto trees = [[atoi("" ~ ch) for ch in line] for line in lines]; 13 | auto height = cast(int) trees.length; 14 | auto width = cast(int) trees[0].length; 15 | bool mut[] visible = new bool mut[](height * width); 16 | for (y in 0 .. height) { 17 | mut int maxl = -1; 18 | for (x in 0 .. width) { 19 | auto tree = trees[y][x]; 20 | if (tree > maxl) { maxl = tree; visible[y * width + x] = true; } 21 | } 22 | mut int maxr = -1; 23 | for (mut int x = width - 1; x >= 0; x--) { 24 | auto tree = trees[y][x]; 25 | if (tree > maxr) { maxr = tree; visible[y * width + x] = true; } 26 | } 27 | } 28 | for (x in 0 .. width) { 29 | mut int maxu = -1; 30 | for (y in 0 .. height) { 31 | auto tree = trees[y][x]; 32 | if (tree > maxu) { maxu = tree; visible[y * width + x] = true; } 33 | } 34 | mut int maxd = -1; 35 | for (mut int y = height - 1; y >= 0; y--) { 36 | auto tree = trees[y][x]; 37 | if (tree > maxd) { maxd = tree; visible[y * width + x] = true; } 38 | } 39 | } 40 | int visible = [sum 1 if v else 0 for v in visible]; 41 | print("$visible trees are visible."); 42 | int scenicScore(int x, int y) { 43 | mut int h = trees[y][x]; 44 | mut int scenicR, scenicL, scenicU, scenicD; 45 | for (mut int x2 = x + 1; x2 < width; x2++) { 46 | scenicR++; 47 | if (trees[y][x2] >= h) break; 48 | } 49 | for (mut int x2 = x - 1; x2 >= 0; x2--) { 50 | scenicL++; 51 | if (trees[y][x2] >= h) break; 52 | } 53 | for (mut int y2 = y + 1; y2 < height; y2++) { 54 | scenicD++; 55 | if (trees[y2][x] >= h) break; 56 | } 57 | for (mut int y2 = y - 1; y2 >= 0; y2--) { 58 | scenicU++; 59 | if (trees[y2][x] >= h) break; 60 | } 61 | // print("$x, $y: score $scenicU * $scenicL * $scenicR * $scenicD"); 62 | return scenicR * scenicL * scenicU * scenicD; 63 | } 64 | int score = [max [max scenicScore(x, y) for x in 0 .. width] for y in 0 .. height]; 65 | print("peak scenic score $score"); 66 | } 67 | -------------------------------------------------------------------------------- /demos/advent22/day9.nt: -------------------------------------------------------------------------------- 1 | module day9; 2 | 3 | macro import std.macro.assert; 4 | macro import std.macro.listcomprehension; 5 | 6 | import std.algorithm; 7 | import std.math; 8 | import std.stdio; 9 | import std.string; 10 | 11 | alias vec2i = Vector(int, 2); 12 | 13 | void main() 14 | { 15 | auto dirToVec = dir => dir.case( 16 | "U": vec2i(-1, 0), 17 | "D": vec2i(1, 0), 18 | "L": vec2i(0, -1), 19 | "R": vec2i(0, 1)); 20 | auto cmds = stdin.byLine 21 | .filter(a => !a.empty) 22 | .map(line => line.split(" ").(dir=that[0].dirToVec, count=that[1].atoi)) 23 | .array; 24 | mut vec2i head = vec2i(0, 0), tail = vec2i(0, 0); 25 | mut bool[vec2i] touched; 26 | void touch(vec2i p) { touched[p] = true; } 27 | vec2i follow(vec2i tail, vec2i head) { 28 | auto d = head - tail; 29 | auto max = d.(max(x.abs, y.abs)), min = d.(min(x.abs, y.abs)); 30 | if (max <= 1) return tail; // touching 31 | if (max == min) { 32 | // clean diagonal move 33 | return tail + d / 2; 34 | } else if (max == 2 && min == 1) { 35 | // skewed diagonal move 36 | auto fixup = i => i.case(-2: -1, -1: -1, 1: 1, 2: 1); 37 | return tail + vec2i(fixup(d.x), fixup(d.y)); 38 | } else { 39 | // straight move 40 | assert(max == 2 && min == 0); 41 | return tail + d / 2; 42 | } 43 | } 44 | for (cmd in cmds) { 45 | for (_ in 0 .. cmd.count) { 46 | head += cmd.dir; 47 | tail = tail.follow(head); 48 | touch(tail); 49 | } 50 | } 51 | print("Visited $(touched.keys.length) positions."); 52 | touched.clear; 53 | auto nodes = [vec2i(0, 0) for i in 0 .. 10].dup; 54 | for (cmd in cmds) { 55 | for (_ in 0 .. cmd.count) { 56 | nodes[0] += cmd.dir; 57 | for (i in 1 .. nodes.length) { 58 | nodes[i] = nodes[i].follow(nodes[i - 1]); 59 | } 60 | 61 | touch(nodes[$ - 1]); 62 | } 63 | } 64 | print("Extended visited $(touched.keys.length) positions."); 65 | } 66 | -------------------------------------------------------------------------------- /demos/alsatest.nt: -------------------------------------------------------------------------------- 1 | module alsatest; 2 | 3 | macro import std.macro.cimport; 4 | 5 | import c_header("alsa/asoundlib.h"); 6 | import std.math; 7 | import std.stdio; 8 | import std.string; 9 | 10 | pragma(lib, "asound"); 11 | 12 | void main() { 13 | mut snd_pcm_t* pcm_handle; 14 | snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK; 15 | mut snd_pcm_hw_params_t* hwparams; 16 | string pcm_name = "default"; 17 | 18 | snd_pcm_hw_params_malloc(&hwparams); 19 | if (snd_pcm_open(&pcm_handle, pcm_name.toStringz, stream, 0) < 0) { 20 | print("Error opening PCM device $pcm_name"); 21 | return; 22 | } 23 | if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) { 24 | print("Can not configure this PCM device."); 25 | return; 26 | } 27 | int rate = 44100; 28 | int periods = 2; 29 | snd_pcm_uframes_t periodsize = 8192; 30 | if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) { 31 | print("Error setting access."); 32 | return; 33 | } 34 | if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE) < 0) { 35 | print("Error setting format."); 36 | return; 37 | } 38 | mut int exact_rate = rate; 39 | if (snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_rate, null) < 0) { 40 | print("Error setting rate."); 41 | return; 42 | } 43 | if (rate != exact_rate) { 44 | print("The rate $rate hz is not supported by your hardware. Closest: $exact_rate"); 45 | return; 46 | } 47 | if (snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 2) < 0) { 48 | print("Error setting channels."); 49 | return; 50 | } 51 | if (snd_pcm_hw_params_set_periods(pcm_handle, hwparams, periods, 0) < 0) { 52 | print("Error setting periods."); 53 | return; 54 | } 55 | // latency = periodsize * periods / (rate * bytes_per_frame); 56 | if (snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, (periodsize * periods) >> 2) < 0) { 57 | print("Error setting buffersize."); 58 | return; 59 | } 60 | if (snd_pcm_hw_params(pcm_handle, hwparams) < 0) { 61 | print("Error setting hw params."); 62 | return; 63 | } 64 | int frames = cast(int) (periodsize >> 2); 65 | ubyte mut[] data = new ubyte mut[](periodsize); 66 | float tri(float f) { 67 | return f - cast(int) f; 68 | } 69 | for (l1 in 0 .. 1000) { 70 | for (l2 in 0 .. frames) { 71 | int l1 = cast(int) l1, l2 = cast(int) l2; 72 | int t = l1 * frames + l2; 73 | // int s = ; 74 | // int s = ((t/2*(15&(0x234568a0>>((t>>8)&28))))|((t/2)>>(t>>11)^((t/16)&t&24))); 75 | float n = l1/10%4-50-(l1/80%2*5)+(l1/320%5*6); 76 | int a = cast(int) (sin(t*pow(2.0f, n/12)) * 1000); 77 | int b = cast(int) (sin(t*pow(2.0f, (n+7)/12)) * 1000); 78 | mut short s1 = cast(short) (a+b); 79 | mut short s2 = cast(short) (a+b); 80 | data[4 * l2 + 0] = (cast(ubyte*) &s1)[0]; 81 | data[4 * l2 + 1] = (cast(ubyte*) &s1)[1]; 82 | data[4 * l2 + 2] = (cast(ubyte*) &s2)[0]; 83 | data[4 * l2 + 3] = (cast(ubyte*) &s2)[1]; 84 | } 85 | while (true) { 86 | long pcmreturn = snd_pcm_writei(pcm_handle, data.ptr, frames); 87 | if (pcmreturn >= 0) break; 88 | snd_pcm_prepare(pcm_handle); 89 | print("BUFFER UNDERRUN!"); 90 | } 91 | } 92 | snd_pcm_drop(pcm_handle); 93 | snd_pcm_drain(pcm_handle); 94 | } 95 | -------------------------------------------------------------------------------- /demos/fib.nt: -------------------------------------------------------------------------------- 1 | module fib; 2 | 3 | macro import std.macro.assert; 4 | 5 | import std.stdio; 6 | import std.string : atoi; 7 | 8 | int fib(int i) { 9 | // Memoize args. 10 | mut int[int] hash; 11 | hash[0] = 0; 12 | hash[1] = 1; 13 | int fib_(int i) { 14 | return hash.require(i, fib_(i - 1) + fib_(i - 2)); 15 | } 16 | return fib_(i); 17 | } 18 | 19 | void main(string[] args) { 20 | if (args.length != 2) { print("expected int parameter"); assert(false); } 21 | int param = args[1].atoi; 22 | print("fib($param) = $(fib(param))"); 23 | } 24 | -------------------------------------------------------------------------------- /demos/gtkdemo/package-lock.json: -------------------------------------------------------------------------------- 1 | {"https://github.com/neat-lang/gtk": "0.0.12"} -------------------------------------------------------------------------------- /demos/gtkdemo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": "src", 3 | "type": "binary", 4 | "binary": "gtkdemo", 5 | "main": "src/main.nt", 6 | "dependencies": { 7 | "gtk": ">=0.0.9" 8 | }, 9 | "sources": { 10 | "gtk": "https://github.com/neat-lang/gtk" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /demos/gtkdemo/src/main.nt: -------------------------------------------------------------------------------- 1 | module main; 2 | 3 | import gtk; 4 | import std.stdio; 5 | 6 | void main() { 7 | gtk_init(null, null); 8 | auto window = Window.new(GTK_WINDOW_TOPLEVEL); 9 | window.setTitle("Window"); 10 | window.setDefaultSize(200, 200); 11 | void quit() { gtk_main_quit; } 12 | connectSignal(window, "destroy", new &quit); 13 | 14 | void printHello() { print("Hello World"); } 15 | auto hbox = HBox.new(homogenous=true, spacing=0); 16 | auto vbox = VBox.new(homogenous=true, spacing=0); 17 | vbox.packStart(hbox, expand=false, fill=false, padding=0); 18 | 19 | auto button1 = Button.new_with_label("Hello"); 20 | connectSignal(button1, "clicked", new &printHello); 21 | hbox.packStart(button1, expand=false, fill=true, padding=0); 22 | 23 | auto button2 = Button.new_with_label("Goodbye"); 24 | connectSignal(button2, "clicked", new &quit); 25 | hbox.packStart(button2, expand=false, fill=true, padding=0); 26 | 27 | window.add(vbox); 28 | 29 | window.show_all; 30 | gtk_main; 31 | } 32 | -------------------------------------------------------------------------------- /demos/halma/.gitignore: -------------------------------------------------------------------------------- 1 | halma 2 | -------------------------------------------------------------------------------- /demos/halma/package-lock.json: -------------------------------------------------------------------------------- 1 | {"https://github.com/neat-lang/gtk": "0.0.12"} -------------------------------------------------------------------------------- /demos/halma/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": "src", 3 | "type": "binary", 4 | "binary": "halma", 5 | "main": "src/main.nt", 6 | "dependencies": { 7 | "gtk": ">=0.0.10" 8 | }, 9 | "sources": { 10 | "gtk": "https://github.com/neat-lang/gtk" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /demos/hellogl.nt: -------------------------------------------------------------------------------- 1 | module hellogl; 2 | 3 | macro import std.macro.cimport; 4 | import c_header("GL/gl.h"); 5 | import c_header("GL/glut.h"); 6 | 7 | version (windows) { 8 | pragma(lib, "opengl32"); 9 | pragma(lib, "freeglut"); 10 | } else { 11 | pragma(lib, "GL"); 12 | pragma(lib, "glut"); 13 | } 14 | 15 | void displayMe() 16 | { 17 | glClear(GL_COLOR_BUFFER_BIT); 18 | glBegin(GL_POLYGON); 19 | glVertex3f(0.0f, 0.0f, 0.0f); 20 | glVertex3f(0.5f, 0.0f, 0.0f); 21 | glVertex3f(0.5f, 0.5f, 0.0f); 22 | glVertex3f(0.0f, 0.5f, 0.0f); 23 | glEnd(); 24 | glFlush(); 25 | } 26 | 27 | void main(string[] args) 28 | { 29 | mut int argc; 30 | char **argv; 31 | glutInit(&argc, argv); 32 | glutInitDisplayMode(0); 33 | glutInitWindowSize(300, 300); 34 | glutCreateWindow("Hello world from Badprog.com :D".ptr); 35 | glutDisplayFunc(&displayMe); 36 | glutMainLoop(); 37 | } 38 | -------------------------------------------------------------------------------- /demos/longestline.nt: -------------------------------------------------------------------------------- 1 | module longestline; 2 | 3 | macro import std.macro.listcomprehension; 4 | 5 | import std.file; 6 | import std.stdio; 7 | import std.string; 8 | 9 | void main(string[] args) { 10 | auto text = readText(args[1]); 11 | string longestLine = [ 12 | argmax(line.strip.length) line 13 | for line in text.split("\n")]; 14 | print(longestLine); 15 | } 16 | -------------------------------------------------------------------------------- /demos/quine.nt: -------------------------------------------------------------------------------- 1 | module quine; 2 | 3 | import std.stdio; 4 | import std.string; 5 | 6 | void main(string[] args) { 7 | string code = "module quine;LLimport std.stdio;Limport std.string;LLvoid main(string[] args) {L string code = QCQ;L print(code.replace([76], [10]).replace([81], [34]).replace([67], code));L}"; 8 | print(code.replace([76], [10]).replace([81], [34]).replace([67], code)); 9 | } 10 | -------------------------------------------------------------------------------- /demos/sha256sum.nt: -------------------------------------------------------------------------------- 1 | module sha256sum; 2 | 3 | import std.file; 4 | import std.sha256; 5 | import std.string; 6 | 7 | void main(string[] args) { 8 | for (i in 1 .. args.length) { 9 | auto sha256 = new Sha256; 10 | sha256.update(args[i].readFile); 11 | print(sha256.finalize.toHexString ~ " " ~ args[i]); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /demos/shaders/frag.glsl: -------------------------------------------------------------------------------- 1 | #version 450 2 | layout(location = 0) in vec3 fragColor; 3 | layout(location = 0) out vec4 outColor; 4 | void main() { 5 | outColor = vec4(fragColor, 1.0); 6 | } 7 | -------------------------------------------------------------------------------- /demos/shaders/frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neat-Lang/neat/7d39ee6abbd2f407656eab61a499e3cf9a9887b7/demos/shaders/frag.spv -------------------------------------------------------------------------------- /demos/shaders/vert.glsl: -------------------------------------------------------------------------------- 1 | #version 450 2 | layout(location = 0) in vec2 inPosition; 3 | layout(location = 1) in vec3 inColor; 4 | layout(location = 0) out vec3 fragColor; 5 | void main() { 6 | gl_Position = vec4(inPosition, 0.0, 1.0); 7 | fragColor = inColor; 8 | } 9 | -------------------------------------------------------------------------------- /demos/shaders/vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neat-Lang/neat/7d39ee6abbd2f407656eab61a499e3cf9a9887b7/demos/shaders/vert.spv -------------------------------------------------------------------------------- /demos/some_grass_or_we.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neat-Lang/neat/7d39ee6abbd2f407656eab61a499e3cf9a9887b7/demos/some_grass_or_we.png -------------------------------------------------------------------------------- /doc/compilerbase.md: -------------------------------------------------------------------------------- 1 | # The CompilerBase Class 2 | 3 | ## Introduction 4 | 5 | In order to avoid circular compiler dependencies, `neat.base` contains a 6 | `CompilerBase` class that is implemented in `main` and passed through 7 | to all compiler calls. 8 | 9 | ## Rationale 10 | 11 | Normally, the compiler classes would be divided into cleanly separated modules. 12 | However, compilers are annoyingly interconnected. When parsing, most more 13 | involved parsed constructs will require looping back into statement/expression 14 | parsing. Similarly, AST and runtime classes are usually expressed in terms of 15 | other classes, which again introduces cycles. For example, the array feature requires 16 | generating an array-append function that loops over source elements, which pulls 17 | in functions, scopes, loops, etc. 18 | 19 | To resolve this, the compiler defines `CompilerBase` as a generic interface to the 20 | rest of the compiler. This class allows parsing source, creating AST trees, or 21 | creating IR trees. This works because usually when we're creating an IR or AST 22 | object, we don't particularly care about it for its members or methods, but just use 23 | it to represent a certain behavioral meaning in the program tree. So we don't care 24 | that an astIndexAccess call creates an ASTIndexAccess class, so much as an ASTSymbol 25 | class that happens to represent an index access. 26 | 27 | ## Self-Hosting 28 | 29 | In the context of macros called inside the compiler, the existence of `CompilerBase` 30 | leads to a snag. 31 | 32 | Since the macro will be pulling in the original compiler's version of `CompilerBase`, 33 | we cannot use new `Compiler` features immediately. Instead, use the version statement 34 | to make the macro work on both bootstrap and current versions of the compiler. 35 | -------------------------------------------------------------------------------- /doc/generations.md: -------------------------------------------------------------------------------- 1 | # \_\_GENERATION\_\_ and bootstrapping 2 | 3 | Any self-hosting static-typed compiled language with macros in the source tree faces a problem. 4 | 5 | Macros need access to the compiler source code in order to be API compatible 6 | with the compiler they're loaded into. But the compiler source changes 7 | in the course of development. So we're looking at at least two build steps: 8 | 9 | - old compiler builds stage1 compiler with new source but old API 10 | - stage1 compiler builds stage2 compiler with new source and new API. 11 | 12 | Neat has several parts that make this process more manageable. 13 | 14 | ## Packages 15 | 16 | First, every module exists inside a package. So neat.base can exist both in 17 | package "compiler" and package "stage1" as two separate files, allowing macros 18 | to reference the specific version for the API of the compiler currently running. 19 | 20 | The package of the running compiler is called `compiler`. So a macro running 21 | `import package(compiler).neat.base` will always see the file matching the API 22 | of the currently running compiler. 23 | 24 | However, that alone would not be enough. Because the mangling of symbols 25 | involves packages, and the mangling of symbols must stay stable for the 26 | same source file during a build (because otherwise dynamic casts will break), 27 | we **cannot** for instance build the new compiler as package "stage1" and then 28 | rename it to "compiler" to build stage2. 29 | 30 | ## \_\_GENERATION\_\_ 31 | 32 | There is a global variable, `__GENERATION__`, that counts up by one every time 33 | the compiler builds itself (via the flag `-next-generation` in `build.sh`). 34 | When we use the package "compiler", the actual mangling is 35 | `compiler__GENERATION__`. As such, the build script can use a commandline flag 36 | (`-print-generation`) to discover the current "compiler" mangling, then just 37 | set the new source code to be built in package `compiler$(__GENERATION__ + 1)`. 38 | Which will be the correct mangling when that source code gets defined as 39 | package "compiler" to build stage2. 40 | -------------------------------------------------------------------------------- /doc/index.md: -------------------------------------------------------------------------------- 1 | # Notes on Neat Compiler Development 2 | 3 | Neat tries to do the simple, straightforward thing in every case. 4 | Nonetheless, during development, several lessons were learnt, 5 | leading to late changes in functionality. 6 | 7 | The goal of these documents is to document those cases 8 | and give insight into their motivations. 9 | 10 | ## Index 11 | 12 | - [The CompilerBase Class](./compilerbase.md) 13 | - [\_\_GENERATION\_\_ and bootstrapping](./generations.md) 14 | - [lambda quarantine checks](./quarantine.md) 15 | - TODO Multi-Stage Feature Transitions 16 | -------------------------------------------------------------------------------- /doc/quarantine.md: -------------------------------------------------------------------------------- 1 | # Lambda Quarantine Checks 2 | 3 | Reminder: there are (basically) three classes of lifetimes 4 | 5 | - lexical: scope allocated, scope cleaned, `auto a = 5`, also covers parameters 6 | - gifted: caller cleans, like `new Object` 7 | - ephemeral: temporary scope, will be cleaned up by someone else some time 8 | - permanent/none: globals. 9 | 10 | To convert from lexical to gifted, we invoke `copy`. To construct a struct, we must convert to gifted, 11 | thus we invoke `copy`. 12 | 13 | Can lambdas be covered by these? No. 14 | 15 | The logic for non-heap-allocated lambdas is driven by one case: 16 | 17 | ``` 18 | auto map(Range, Lambda)(Range range, Lambda lambda) { 19 | struct MapRange { 20 | ... 21 | } 22 | return MapRange(range, lambda); 23 | } 24 | ``` 25 | 26 | This should not require allocations! But it constructs a struct, it returns a value. 27 | Do we need full lifetime tracking after all? 28 | 29 | Still no. 30 | 31 | # Dynamic Quarantine 32 | 33 | What we want is, given a lambda expression, `a => ...`, for it to be confined to a "dynamic quarantine": 34 | that is, the stack space dynamically beneath the expression. We don't care about assignments, 35 | constructions, parameter passing, so long as we cannot leave the quarantine area through them. 36 | 37 | What is actually *unsafe*? 38 | 39 | - Assignment to a class, array or hashmap field (any type that has its own refcounting) 40 | - Return from the declaring function. 41 | 42 | # Return checking 43 | 44 | We introduce a method: `checkQuarantine` on `Type`. Right now, this only gates returning values. 45 | 46 | We don't want to prevent returning from a function that's dynamically beneath the declaration. We only want 47 | to prevent return from the function that the lambda actually captures. How do we disambiguate? 48 | 49 | The lambda already knows which function declared it. So we can just check when returning from a function 50 | if that function is the declaring function, and if so, error. This will work so long as 51 | the quarantine is otherwise preserved. 52 | 53 | # Quarantine policy 54 | 55 | Assignment can be checked at the type level with `QuarantinePolicy`. 56 | 57 | A container type indicates: 58 | 59 | - transparent, if the value is guaranteed to recurse into the assigned field in `checkQuarantine`, for 60 | example structs. 61 | - occluded, if the value may hide an assigned field on return, for example classes. 62 | 63 | A value type indicates: 64 | 65 | - checked, if we need to check quarantine on it, ie. lambdas 66 | - harmless, if it uses the normal refcounting. 67 | 68 | Then, we can just say that assigning a checked value to an occluded field is an error. 69 | 70 | # Recursive functions 71 | 72 | Q: But what about functions that pass a lambda to themselves recursively, and then return it from the 73 | recursive call? 74 | 75 | A: Listen: I don't care about that usecase. This is intended to allow one thing: `map`, and functions like it. 76 | If you can think of a clever (and not excessively complicated) way to do it in this framework, patches 77 | welcome. 78 | -------------------------------------------------------------------------------- /doc/sphinx/_ext/xe_quote.py: -------------------------------------------------------------------------------- 1 | from docutils.parsers.rst import Directive 2 | from docutils.statemachine import ViewList 3 | from docutils import nodes 4 | from docutils.parsers.rst.states import Inliner 5 | 6 | class XeQuote(Directive): 7 | required_arguments = 1 8 | optional_arguments = 2 9 | has_content = True 10 | 11 | def run(self): 12 | persona = self.arguments[0] if self.arguments else None 13 | mood = self.arguments[1] if len(self.arguments) > 1 else None 14 | blockquote = nodes.block_quote() 15 | blockquote.set_class('xe-quote') 16 | image_node = nodes.image(**self.image_uri(persona, mood)) 17 | nested_node = nodes.container() 18 | 19 | content = ViewList(self.content) 20 | self.state.nested_parse(content, 0, node=nested_node) 21 | 22 | blockquote += image_node 23 | blockquote += nested_node.children 24 | return [blockquote] 25 | 26 | def image_uri(self, persona, mood): 27 | if persona == 'shoebill': 28 | mapping = { 29 | 'neutral': '/_static/breakelse/shoebill_neutral.png', 30 | 'aghast': '/_static/breakelse/shoebill_aghast.png', 31 | 'considering': '/_static/breakelse/shoebill_considering.png', 32 | } 33 | elif persona == 'gurkenglas': 34 | mapping = { 35 | 'neutral': '/_static/breakelse/gurkenglas_neutral.png', 36 | 'suggesting': '/_static/breakelse/gurkenglas_suggesting.png', 37 | 'looking': '/_static/breakelse/gurkenglas_looking.png', 38 | 'idea': '/_static/breakelse/gurkenglas_idea.png', 39 | 'unimpressed': '/_static/breakelse/gurkenglas_unimpressed.png', 40 | } 41 | else: 42 | assert False 43 | mood = mood or 'neutral' 44 | return { 45 | 'uri': mapping[mood], 46 | 'alt': f'{persona} is {mood}', 47 | } 48 | 49 | 50 | def setup(app): 51 | app.add_directive('xe-quote', XeQuote) 52 | 53 | return { 54 | 'version': '0.1', 55 | 'parallel_read_safe': True, 56 | 'parallel_write_safe': True, 57 | } 58 | 59 | -------------------------------------------------------------------------------- /doc/sphinx/_static/breakelse/gurkenglas_idea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neat-Lang/neat/7d39ee6abbd2f407656eab61a499e3cf9a9887b7/doc/sphinx/_static/breakelse/gurkenglas_idea.png -------------------------------------------------------------------------------- /doc/sphinx/_static/breakelse/gurkenglas_looking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neat-Lang/neat/7d39ee6abbd2f407656eab61a499e3cf9a9887b7/doc/sphinx/_static/breakelse/gurkenglas_looking.png -------------------------------------------------------------------------------- /doc/sphinx/_static/breakelse/gurkenglas_neutral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neat-Lang/neat/7d39ee6abbd2f407656eab61a499e3cf9a9887b7/doc/sphinx/_static/breakelse/gurkenglas_neutral.png -------------------------------------------------------------------------------- /doc/sphinx/_static/breakelse/gurkenglas_suggesting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neat-Lang/neat/7d39ee6abbd2f407656eab61a499e3cf9a9887b7/doc/sphinx/_static/breakelse/gurkenglas_suggesting.png -------------------------------------------------------------------------------- /doc/sphinx/_static/breakelse/gurkenglas_unimpressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neat-Lang/neat/7d39ee6abbd2f407656eab61a499e3cf9a9887b7/doc/sphinx/_static/breakelse/gurkenglas_unimpressed.png -------------------------------------------------------------------------------- /doc/sphinx/_static/breakelse/shoebill_aghast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neat-Lang/neat/7d39ee6abbd2f407656eab61a499e3cf9a9887b7/doc/sphinx/_static/breakelse/shoebill_aghast.png -------------------------------------------------------------------------------- /doc/sphinx/_static/breakelse/shoebill_considering.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neat-Lang/neat/7d39ee6abbd2f407656eab61a499e3cf9a9887b7/doc/sphinx/_static/breakelse/shoebill_considering.png -------------------------------------------------------------------------------- /doc/sphinx/_static/breakelse/shoebill_neutral.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Neat-Lang/neat/7d39ee6abbd2f407656eab61a499e3cf9a9887b7/doc/sphinx/_static/breakelse/shoebill_neutral.png -------------------------------------------------------------------------------- /doc/sphinx/_static/custom.css: -------------------------------------------------------------------------------- 1 | .sphinxsidebarwrapper > h3 { 2 | display: none; 3 | } 4 | 5 | #searchlabel { 6 | display: none; 7 | } 8 | 9 | #searchbox::before { 10 | content: "Search"; 11 | font-size: 1.5em; 12 | } 13 | 14 | #searchbox form { 15 | margin: 0; 16 | } 17 | 18 | .xe-quote { 19 | border: 1px solid #ccc; 20 | padding: 10px; 21 | margin-left: 2px; 22 | background-color: #fffff0; 23 | position: relative; 24 | } 25 | 26 | .xe-quote p { 27 | margin: 0; 28 | } 29 | 30 | .xe-quote img { 31 | float: left; 32 | width: 5em; 33 | height: 4em; 34 | object-fit: cover; 35 | object-position: top; 36 | margin-right: 1em; 37 | } 38 | 39 | /* ~ */ 40 | hr { 41 | border: none; 42 | height: 0; 43 | background-color: transparent; 44 | border-top: 0; 45 | text-align: center; 46 | margin-top: -0.2em; 47 | margin-bottom: 1.5em; 48 | } 49 | 50 | hr::before { 51 | content: "~"; 52 | font-size: 3em; 53 | line-height: 0.1em; 54 | letter-spacing: 0.6em; 55 | color: #ccc; 56 | padding: 0.2em 0.6em; 57 | } 58 | 59 | .rant-gets-smaller span { 60 | font-size: 92%; 61 | } 62 | -------------------------------------------------------------------------------- /doc/sphinx/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | import os 14 | import sys 15 | sys.path.insert(0, os.path.abspath('./_ext')) 16 | 17 | # -- Project information ----------------------------------------------------- 18 | 19 | project = 'Neat' 20 | copyright = '@FeepingCreature' 21 | author = '@FeepingCreature' 22 | 23 | 24 | # -- General configuration --------------------------------------------------- 25 | 26 | # Add any Sphinx extension module names here, as strings. They can be 27 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 28 | # ones. 29 | extensions = [ 30 | 'sphinx_rtd_theme', 31 | 'sphinx.ext.autosectionlabel', 32 | 'xe_quote', 33 | ] 34 | 35 | autosectionlabel_prefix_document = True 36 | 37 | # Add any paths that contain templates here, relative to this directory. 38 | templates_path = ['_templates'] 39 | 40 | # List of patterns, relative to source directory, that match files and 41 | # directories to ignore when looking for source files. 42 | # This pattern also affects html_static_path and html_extra_path. 43 | exclude_patterns = [] 44 | 45 | default_role = 'code' 46 | 47 | # -- Options for HTML output ------------------------------------------------- 48 | 49 | # The theme to use for HTML and HTML Help pages. See the documentation for 50 | # a list of builtin themes. 51 | # 52 | html_theme = 'alabaster' 53 | 54 | # Add any paths that contain custom static files (such as style sheets) here, 55 | # relative to this directory. They are copied after the builtin static files, 56 | # so a file named "default.css" will overwrite the builtin "default.css". 57 | html_static_path = ['_static'] 58 | 59 | 60 | html_permalinks_icon = '¶' 61 | html_sidebars = { 62 | '**': ['about.html', 'navigation.html', 'searchbox.html'], 63 | 'dconf_23': [], 64 | } 65 | 66 | -------------------------------------------------------------------------------- /doc/sphinx/dconf_23.rst: -------------------------------------------------------------------------------- 1 | ============================= 2 | The Neat Programming Language 3 | ============================= 4 | 5 | .. image:: https://img.shields.io/badge/license-BSD-blue.svg 6 | :target: https://github.com/neat-lang/neat/blob/master/LICENSE 7 | 8 | .. image:: https://img.shields.io/badge/platform-Linux%2064--bit-brightgreen.svg 9 | 10 | Links 11 | ----- 12 | 13 | `Documentation `_ 14 | 15 | `Github `_ 16 | 17 | `IRC: #neat on libera.chat `_ 18 | 19 | Installation 20 | ------------ 21 | 22 | To set up Neat on Ubuntu 22.04 from source, follow these steps: 23 | 24 | .. code-block:: bash 25 | 26 | apt-get update 27 | apt-get install -y --no-install-recommends \ 28 | ca-certificates clang-15 curl file gcc git llvm-15 llvm-15-dev unzip 29 | git clone https://github.com/neat-lang/neat 30 | cd neat 31 | ./build.sh 32 | export PATH=$(pwd)/build:$PATH 33 | 34 | Note that copying the binary to `/usr/local/bin` will *not* work at present, 35 | as `neat` looks for configuration and compiler source relative to the binary. 36 | 37 | Sample 38 | ------ 39 | 40 | To run a sample: 41 | 42 | .. code-block:: bash 43 | 44 | neat demos/longestline.nt 45 | ./longestline demos/longestline.nt 46 | 47 | Check the other files in `demos/` as well, but note that they may need various `-dev` packages installed. 48 | 49 | Help, things stopped working! 50 | ----------------------------- 51 | 52 | If compiles are randomly failing with linker errors or weird problems, delete the `.obj/` folder, 53 | which caches compilation artifacts. 54 | 55 | This should not happen, and yet... 56 | -------------------------------------------------------------------------------- /doc/sphinx/index.rst: -------------------------------------------------------------------------------- 1 | .. highlight:: d 2 | 3 | The Neat Language 4 | ================= 5 | 6 | Neat is a C-like statically-typed native compiled language with automated reference counting, OOP and macros. 7 | Its syntax is heavily "inspired by" D. 8 | 9 | Have some example code! Here's a program that prints the longest line in a file:: 10 | 11 | module longestline; 12 | 13 | macro import std.macro.listcomprehension; 14 | 15 | import std.file; 16 | import std.stdio; 17 | import std.string; 18 | 19 | void main(string[] args) { 20 | auto text = readText(args[1]); 21 | string longestLine = [ 22 | argmax(line.strip.length) line 23 | for line in text.split("\n")]; 24 | print(longestLine); 25 | } 26 | ... 27 | $ neat longestline.nt 28 | ... 29 | $ ./longestline longestline.nt 30 | macro import std.macro.listcomprehension; 31 | 32 | **Important notice**: Before you jump in, please read the section :ref:`getstarted:Good and Bad Neat`! 33 | 34 | For **more examples,** see the `Demos `_ page. 35 | 36 | Community 37 | ========= 38 | 39 | - `Neat Language on Discord `_ 40 | - `#neat on libera.chat `_ (formerly Freenode). 41 | - If you're trying out the language, drop by and say hi! 42 | - `Github Discussions `_ 43 | 44 | Contents 45 | ======== 46 | 47 | .. toctree:: 48 | :maxdepth: 2 49 | 50 | getstarted 51 | intro 52 | manual 53 | std 54 | Neat on Github 🔗 55 | -------------------------------------------------------------------------------- /docgen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | find doc/sphinx/std/ -name \*.rst |xargs rm 3 | find src/std/ -name \*.nt |\ 4 | xargs -P 8 -I {} sh -c 'neat "$0" -c -docgen doc/sphinx/' {} 5 | summary() { 6 | cat <<-EOT 7 | .. _std: 8 | .. highlight:: d 9 | 10 | Standard Library 11 | ================= 12 | 13 | .. toctree:: 14 | :maxdepth: 2 15 | :caption: Contents: 16 | :glob: 17 | 18 | std/** 19 | EOT 20 | find src/std/ -name \*.nt |sort |\ 21 | while read file 22 | do 23 | subfile=$(echo "$file" |sed -e 's,^src/,,' -e 's/.nt$//') 24 | mod=$(echo "$subfile" |sed -e 's,/,.,g') 25 | if [ -f doc/sphinx/"$subfile".rst ] 26 | then 27 | echo 28 | echo ".. rubric:: $mod" 29 | echo 30 | echo ".. c:namespace:: $mod" 31 | echo 32 | grep "Module entries" doc/sphinx/"$subfile".rst 33 | fi 34 | done 35 | } 36 | summary > doc/sphinx/std.rst 37 | -------------------------------------------------------------------------------- /find-llvm-config.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | if [ -v LLVM_CONFIG ] 3 | then 4 | : 5 | elif [ -f "/usr/lib/llvm-15/bin/llvm-config" ] 6 | then 7 | LLVM_CONFIG="/usr/lib/llvm-15/bin/llvm-config" 8 | elif [ -f "/usr/lib/llvm/15/bin/llvm-config" ] 9 | then 10 | LLVM_CONFIG="/usr/lib/llvm/15/bin/llvm-config" 11 | elif [ -f "/usr/lib/llvm15/bin/llvm-config" ] 12 | then 13 | LLVM_CONFIG="/usr/lib/llvm15/bin/llvm-config" 14 | else 15 | echo "Cannot find llvm-15 llvm-config!" 16 | exit 1 17 | fi 18 | -------------------------------------------------------------------------------- /neat.kdev4: -------------------------------------------------------------------------------- 1 | [Project] 2 | Name=neat 3 | Manager=KDevCustomMakeManager 4 | -------------------------------------------------------------------------------- /release-llvm.sh: -------------------------------------------------------------------------------- 1 | release-gcc.sh -------------------------------------------------------------------------------- /release-win64-gcc.sh: -------------------------------------------------------------------------------- 1 | release-gcc.sh -------------------------------------------------------------------------------- /runtests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | NEAT=${NEAT:-build/neat} 4 | NEATFLAGS="${NEATFLAGS:-}" 5 | NEATFLAGS="${NEATFLAGS} -Pimports:test/imports" 6 | NEATFLAGS="${NEATFLAGS} -Prunnable:test/runnable:compiler,imports" 7 | NEATFLAGS="${NEATFLAGS} -Pfail_compilation:test/fail_compilation:compiler,imports" 8 | WINEMODE=0 9 | 10 | if [ "${1:-}" = "win64" ] 11 | then 12 | WINEMODE=1 13 | NEATFLAGS="${NEATFLAGS} -target x86_64-w64-mingw32" 14 | export WINEDEBUG=-all 15 | shift 16 | fi 17 | 18 | num_total=$(ls -q test/runnable/*.nt test/fail_compilation/*.nt |wc -l) 19 | build_failed=0 20 | run_failed=0 21 | build_crashed=0 22 | falsely_succeeded=0 23 | test_skipped=0 24 | 25 | function test_enabled { 26 | test="$1" 27 | shift 28 | if [ $# -eq 0 ]; then return 0; fi 29 | while [ $# -gt 0 ] 30 | do 31 | if [[ "$test" == *"$1"* ]]; then return 0; fi 32 | shift 33 | done 34 | return 1 35 | } 36 | 37 | # runnable 38 | mkdir -p build/test/runnable 39 | while read file 40 | do 41 | if ! test_enabled "$file" "$@" 42 | then 43 | test_skipped=$((test_skipped+1)) 44 | continue 45 | fi 46 | echo "$file"... 47 | executable=build/"$file" 48 | run=$executable 49 | if [ $WINEMODE -eq 1 ]; then 50 | executable="${executable}.exe" 51 | run="wine $executable" 52 | fi 53 | CMD="$NEAT $NEATFLAGS \"$file\" -o \"$executable\"" 54 | if ! eval $CMD 2>&1 |cat>build/out.txt 55 | then 56 | build_failed=$((build_failed+1)) 57 | echo $CMD 58 | cat build/out.txt 59 | elif ! sh -c "$run" 60 | then 61 | run_failed=$((run_failed+1)) 62 | fi 63 | done < <(ls -q test/runnable/*.nt) 64 | 65 | # fail_compilation 66 | # tests should fail with an exit code, not a segfault 67 | mkdir -p build/test/fail_compilation 68 | while read file 69 | do 70 | if ! test_enabled "$file" "$@" 71 | then 72 | test_skipped=$((test_skipped+1)) 73 | continue 74 | fi 75 | echo "$file"... 76 | executable=build/"$file" 77 | CMD="$NEAT $NEATFLAGS \"$file\" -o \"$executable\"" 78 | set +e 79 | eval $CMD 2>&1 |cat>build/out.txt 80 | EXIT=$? 81 | set -e 82 | if [ $EXIT -eq 0 ]; then 83 | falsely_succeeded=$((falsely_succeeded+1)) 84 | echo $CMD 85 | cat build/out.txt 86 | echo "Error expected but not found!" 87 | elif [ $EXIT -gt 128 ]; then 88 | # signal 89 | build_crashed=$((build_crashed+1)) 90 | echo $CMD 91 | cat build/out.txt 92 | fi 93 | done < <(ls -q test/fail_compilation/*.nt) 94 | 95 | num_total=$((num_total - test_skipped)) 96 | num_success=$((num_total - build_failed - run_failed - falsely_succeeded - build_crashed)) 97 | 98 | echo "Result:" 99 | echo " ${num_success}/${num_total} successful" 100 | echo " ${build_failed} failed to build" 101 | echo " ${run_failed} failed to run" 102 | echo " ${falsely_succeeded} built when they should have failed" 103 | echo " ${build_crashed} crashed the compiler instead of erroring" 104 | echo " ${test_skipped} tests were skipped" 105 | [ $num_success -eq $num_total ] 106 | -------------------------------------------------------------------------------- /src/c/fcntl.nt: -------------------------------------------------------------------------------- 1 | module c.fcntl; 2 | 3 | extern(C) int open(char*, int flags, int mode); 4 | 5 | alias O_RDONLY = 0; 6 | alias O_WRONLY = 1; 7 | alias O_RDWR = 2; 8 | 9 | version (windows) { 10 | alias O_CREAT = 0x100; 11 | } else { 12 | alias O_CREAT = 0x040; 13 | } 14 | 15 | alias S_IWOTH = 2; 16 | alias S_IROTH = 4; 17 | alias S_IWGRP = 16; 18 | alias S_IRGRP = 32; 19 | alias S_IWUSR = 128; 20 | alias S_IRUSR = 256; 21 | -------------------------------------------------------------------------------- /src/c/libgen.nt: -------------------------------------------------------------------------------- 1 | module c.libgen; 2 | 3 | extern(C) char* dirname(char* path); 4 | extern(C) char* basename(char* path); 5 | -------------------------------------------------------------------------------- /src/c/pthread.nt: -------------------------------------------------------------------------------- 1 | module c.pthread; 2 | 3 | struct pthread_mutex_t 4 | { 5 | // __SIZEOF_PTHREAD_MUTEX_T is like 40 max 6 | // 64 on Mac! 7 | long a, b, c, d, e, f, g, h; 8 | } 9 | 10 | struct pthread_mutexattr_t 11 | { 12 | // __SIZEOF_PTHREAD_MUTEXATTR_T is 4 13 | // 16 on mac? 14 | long a, b; 15 | } 16 | 17 | extern(C) int pthread_mutex_init(pthread_mutex_t* mutex, pthread_mutexattr_t* attr); 18 | extern(C) int pthread_mutex_destroy(pthread_mutex_t* mutex); 19 | extern(C) int pthread_mutex_lock(pthread_mutex_t* mutex); 20 | extern(C) int pthread_mutex_unlock(pthread_mutex_t* mutex); 21 | 22 | alias PTHREAD_MUTEX_NORMAL = 0; 23 | alias PTHREAD_MUTEX_RECURSIVE = 1; 24 | alias PTHREAD_MUTEX_ERRORCHECK = 2; 25 | 26 | extern(C) int pthread_mutexattr_init(pthread_mutexattr_t* attr); 27 | extern(C) int pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type); 28 | 29 | struct pthread_cond_t 30 | { 31 | // __SIZEOF_PTHREAD_COND_T is like 48? 32 | // ?? 33 | long a, b, c, d, e, f, g, h; 34 | } 35 | 36 | extern(C) int pthread_cond_init(pthread_cond_t*, void* attr); 37 | extern(C) int pthread_cond_destroy(pthread_cond_t*); 38 | extern(C) int pthread_cond_wait(pthread_cond_t*, pthread_mutex_t*); 39 | extern(C) int pthread_cond_broadcast(pthread_cond_t*); 40 | extern(C) int pthread_cond_signal(pthread_cond_t*); 41 | 42 | struct pthread_t 43 | { 44 | // __SIZEOF_PTHREAD_T is 8, I think? 45 | // ??? 46 | long a, b; 47 | } 48 | 49 | // placeholder that's definitely (I'd hope so!) large enough for a pthread_attr_t. 50 | struct pthread_attr_t 51 | { 52 | // __SIZEOF_PTHREAD_ATTR_T?? Iunno? 64? 53 | long a, b, c, d, e, f, g, h; 54 | } 55 | 56 | extern(C) int pthread_create(pthread_t* thread, pthread_attr_t* attr, void function(void*) start_routine, void* arg); 57 | extern(C) int pthread_attr_init(pthread_attr_t* attr); 58 | extern(C) int pthread_attr_setstacksize(pthread_attr_t* attr, size_t stacksize); 59 | 60 | struct pthread_key_t { 61 | int value; 62 | } 63 | 64 | extern(C) int pthread_key_create(pthread_key_t* key, void function(void*) destructor); 65 | extern(C) int pthread_setspecific(pthread_key_t key, void* value); 66 | extern(C) void* pthread_getspecific(pthread_key_t); 67 | -------------------------------------------------------------------------------- /src/c/semaphore.nt: -------------------------------------------------------------------------------- 1 | module semaphore; 2 | 3 | // see /usr/include/bits/semaphore.h 4 | // glibc reserves word * 4 for semaphores. This seems sensible. 5 | struct sem_t { 6 | size_t a, b, c, d; 7 | } 8 | 9 | extern(C) int sem_init(sem_t* sem, int pshared, int value); 10 | extern(C) int sem_destroy(sem_t* sem); 11 | extern(C) int sem_wait(sem_t* sem); 12 | extern(C) int sem_post(sem_t* sem); 13 | -------------------------------------------------------------------------------- /src/c/stdio.nt: -------------------------------------------------------------------------------- 1 | module c.stdio; 2 | 3 | extern(C) int remove(char* path); 4 | extern(C) int rename(char* oldpath, char* newpath); 5 | -------------------------------------------------------------------------------- /src/c/stdlib.nt: -------------------------------------------------------------------------------- 1 | module c.stdlib; 2 | 3 | extern(C) char* realpath(char* path, char* resolved_path); 4 | extern(C) void exit(int status); 5 | extern(C) int setenv(char* name, char* value, int overwrite); 6 | -------------------------------------------------------------------------------- /src/c/sys/stat.nt: -------------------------------------------------------------------------------- 1 | module c.sys.stat; 2 | 3 | import c.sys.time : timespec; 4 | 5 | extern(C) int mkdir(char* pathname, int mode); 6 | 7 | struct stat_struct { 8 | size_t st_dev; 9 | size_t st_ino; 10 | size_t st_nlink; 11 | int st_mode; 12 | int st_uid; 13 | int st_gid; 14 | int _pad0; 15 | size_t st_rdev; 16 | size_t st_size; 17 | size_t st_blksize; 18 | size_t st_blocks; 19 | timespec st_atim; 20 | timespec st_mtim; 21 | timespec st_ctim; 22 | size_t __glibc_reserved1; 23 | size_t __glibc_reserved2; 24 | size_t __glibc_reserved3; 25 | } 26 | 27 | alias S_IFREG = 0x8000; 28 | 29 | extern(C) int stat(char* pathname, stat_struct* statbuf); 30 | -------------------------------------------------------------------------------- /src/c/sys/time.nt: -------------------------------------------------------------------------------- 1 | module c.sys.time; 2 | 3 | alias time_t = long; 4 | 5 | struct timeval 6 | { 7 | time_t tv_sec; 8 | int tv_usec; 9 | } 10 | 11 | struct timespec 12 | { 13 | time_t tv_sec; 14 | size_t tv_nsec; 15 | } 16 | 17 | struct tm 18 | { 19 | int tm_sec; 20 | int tm_min; 21 | int tm_hour; 22 | int tm_mday; 23 | int tm_mon; 24 | int tm_year; 25 | int tm_wday; 26 | int tm_yday; 27 | int tm_isdst; 28 | size_t tm_gmtoff; 29 | char* tm_zone; 30 | } 31 | 32 | alias CLOCK_MONOTONIC = 1; 33 | alias CLOCK_PROCESS_CPUTIME_ID = 2; 34 | alias CLOCK_THREAD_CPUTIME_ID = 3; 35 | alias CLOCK_MONOTONIC_RAW = 4; 36 | alias CLOCK_REALTIME_COARSE = 5; 37 | alias CLOCK_MONOTONIC_COARSE = 6; 38 | 39 | extern(C) int gettimeofday(timeval* tv, void* tz); 40 | extern(C) int clock_gettime(int clockid, timespec* tp); 41 | extern(C) tm* localtime_r(time_t* timep, tm* result); 42 | extern(C) time_t mktime(tm* time); 43 | extern(C) int nanosleep(timespec* req, timespec* rem); 44 | -------------------------------------------------------------------------------- /src/c/unistd.nt: -------------------------------------------------------------------------------- 1 | module c.unistd; 2 | 3 | extern(C) size_t read(int fd, void* buf, size_t count); 4 | extern(C) size_t write(int fd, void* buf, size_t count); 5 | extern(C) int close(int fd); 6 | -------------------------------------------------------------------------------- /src/c/windows.nt: -------------------------------------------------------------------------------- 1 | module c.windows; 2 | 3 | alias HANDLE = void*; 4 | alias WORD = short; 5 | alias DWORD = int; 6 | alias LONG = int; 7 | 8 | extern(C) int CloseHandle(HANDLE hObject); 9 | 10 | extern(C) HANDLE CreateMutexA(void*, bool owned, char* name); 11 | 12 | alias CreateMutex = CreateMutexA; 13 | 14 | struct CRITICAL_SECTION { 15 | // Copied from the Wine headers. 16 | // I'm assuming they're right about the size at least. 17 | void* DebugInfo; 18 | int LockCount; 19 | int RecursionCount; 20 | void* OwningThread; 21 | void* LockSemaphore; 22 | size_t SpinCount; 23 | } 24 | 25 | extern(C) void InitializeCriticalSection(CRITICAL_SECTION*); 26 | extern(C) void DeleteCriticalSection(CRITICAL_SECTION*); 27 | extern(C) void EnterCriticalSection(CRITICAL_SECTION*); 28 | extern(C) void LeaveCriticalSection(CRITICAL_SECTION*); 29 | 30 | extern(C) HANDLE CreateSemaphoreA(void* lpSemaphoreAttributes, LONG lInitialCount, LONG lMaximumCount, 31 | char* name); 32 | extern(C) int ReleaseSemaphore(HANDLE semaphore, LONG lReleaseCount, int* lpPreviousCount); 33 | extern(C) int WaitForSingleObject(HANDLE semaphore, DWORD milliseconds); 34 | 35 | alias INFINITE = -1; 36 | 37 | alias CreateSemaphore = CreateSemaphoreA; 38 | 39 | extern(C) HANDLE CreateThread(void* lpThreadAttributes, DWORD dwStackSize, 40 | void function(void*) lpStartAddress, void* lpParameter, DWORD dwCreationFlags, LONG* lpThreadId); 41 | 42 | extern(C) DWORD TlsAlloc(); 43 | extern(C) void* TlsGetValue(DWORD dwTlsIndex); 44 | extern(C) int TlsSetValue(DWORD dwTlsIndex, void* lpTlsValue); 45 | 46 | alias TLS_OUT_OF_INDEXES = -1; 47 | 48 | struct STARTUPINFOA { 49 | DWORD cb; 50 | char* lpReserved; 51 | char* lpDesktop; 52 | char* lpTitle; 53 | DWORD dwX; 54 | DWORD dwY; 55 | DWORD dwXSize; 56 | DWORD dwYSize; 57 | DWORD dwXCountChars; 58 | DWORD dwYCountChars; 59 | DWORD dwFillAttribute; 60 | DWORD dwFlags; 61 | WORD wShowWindow; 62 | WORD cbReserved2; 63 | ubyte* lpReserved2; 64 | HANDLE hStdInput; 65 | HANDLE hStdOutput; 66 | HANDLE hStdError; 67 | } 68 | 69 | struct PROCESS_INFORMATION { 70 | HANDLE hProcess; 71 | HANDLE hThread; 72 | DWORD dwProcessId; 73 | DWORD dwThreadId; 74 | } 75 | 76 | extern(C) bool CreateProcessA(char* lpApplicationName, char* lpCommandLine, void* lpProcessAttributes, 77 | void* lpThreadAttributes, bool bInheritHandles, DWORD dwCreationFlags, void* lpEnvironment, 78 | char* lpCurrentDirectory, STARTUPINFOA* lpStartupInfo, PROCESS_INFORMATION* lpProcessInformation); 79 | 80 | alias CreateProcess = CreateProcessA; 81 | alias STARTUPINFO = STARTUPINFOA; 82 | 83 | extern(C) DWORD GetFileAttributesA(char* filename); 84 | 85 | alias GetFileAttributes = GetFileAttributesA; 86 | alias INVALID_FILE_ATTRIBUTES = -1; 87 | 88 | extern(C) DWORD GetModuleFileNameA(void* hModule, char* lpFilename, DWORD nSize); 89 | extern(C) char* _fullpath(char* absPath, char* relPath, size_t maxLength); 90 | 91 | alias GetModuleFileName = GetModuleFileNameA; 92 | alias MAX_PATH = 260; 93 | 94 | extern(C) void Sleep(DWORD dwMilliseconds); 95 | -------------------------------------------------------------------------------- /src/neat/bottom.nt: -------------------------------------------------------------------------------- 1 | module neat.bottom; 2 | 3 | import backend.base; 4 | import neat.base; 5 | import neat.runtime.locrange; 6 | import polyhash; 7 | 8 | /* 9 | * bottom type 10 | * =========== 11 | * Bottom : Type, represented by 'bottom'. 12 | * Unreachable : Expression, represented by 'unreachable'. 13 | * Bottom implicitly converts to Unreachable expressions in every type. 14 | * In the backend, `bottom` has a size of 0 and may be represented by `unreachable` or `undef`. 15 | */ 16 | 17 | class Bottom : Type 18 | { 19 | this() { 20 | this.zeroInitializable = true; 21 | this.hasElaborateCopyConstructor = false; 22 | } 23 | override BackendType emit(Platform platform) { 24 | return new BackendVoidType; 25 | } 26 | override bool same(Type type) { 27 | return !!type.instanceOf(Bottom); 28 | } 29 | override string repr() { return "bottom"; } 30 | override string mangle() { return "bottom"; } 31 | override void hash(Hash hash) { 32 | hash.adds("bottom"); 33 | } 34 | override (nullable Expression | Error) implicitConvertTo( 35 | Context context, Expression source, Type target, LocRange locRange) 36 | { 37 | return new UnreachableExpr(target, source); 38 | } 39 | } 40 | 41 | class UnreachableExpr : Expression 42 | { 43 | nullable Expression source; 44 | this(this.type=new Bottom, this.source=null) { this.info = ExprInfo(Lifetime.none); } 45 | override int emit(Generator output) { 46 | if (source) source.emit(output); 47 | output.fun.unreachable; 48 | output.fun.setLabel(output.fun.getLabel ~ "_unreachable"); 49 | return output.fun.zeroLiteral(this.type.emit(output.platform)); 50 | } 51 | override string repr() { 52 | return "unreachable"; 53 | } 54 | override void hash(Hash hash) { 55 | this.type.hash(hash); 56 | hash.adds("unreachable"); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/neat/float.nt: -------------------------------------------------------------------------------- 1 | module neat.float; 2 | 3 | macro import package(compiler).std.macro.once; 4 | 5 | import backend.base; 6 | import neat.base; 7 | import neat.runtime; 8 | import polyhash; 9 | 10 | class Float : Type 11 | { 12 | BackendType type; 13 | this() { 14 | this.type = new BackendFloatType; 15 | this.zeroInitializable = true; 16 | } 17 | 18 | override BackendType emit(Platform platform) { return this.type; } 19 | 20 | override bool same(Type other) { return !!other.instanceOf(Float); } 21 | 22 | override string repr() { return "float"; } 23 | 24 | override string mangle() { return "float"; } 25 | 26 | override (nullable Expression | Error) implicitConvertFrom( 27 | Context context, Expression source, LocRange locRange) 28 | { 29 | if (auto dblLit = source.instanceOf(DoubleLiteral)) 30 | { 31 | return new FloatLiteral(cast(float) dblLit.value); 32 | } 33 | return null; 34 | } 35 | 36 | 37 | override void hash(Hash hash) { hash.adds("float"); } 38 | } 39 | 40 | class Double : Type 41 | { 42 | BackendType type; 43 | this() { 44 | this.type = new BackendDoubleType; 45 | this.zeroInitializable = true; 46 | } 47 | 48 | override BackendType emit(Platform platform) { return this.type; } 49 | 50 | override bool same(Type other) { return !!other.instanceOf(Double); } 51 | 52 | override string repr() { return "double"; } 53 | 54 | override string mangle() { return "double"; } 55 | 56 | override void hash(Hash hash) { hash.adds("double"); } 57 | } 58 | 59 | class ASTFloatLiteral : ASTSymbol 60 | { 61 | float value; 62 | 63 | this(this.value) { } 64 | 65 | override (Symbol | Error) compile(Context context) 66 | { 67 | return new FloatLiteral(this.value); 68 | } 69 | } 70 | 71 | class FloatLiteral : Expression 72 | { 73 | float value; 74 | this(this.value) { this.type = once new Float; this.info = ExprInfo(Lifetime.permanent); } 75 | override int emit(Generator output) { return output.fun.floatLiteral(this.value); } 76 | override void hash(Hash hash) { hash.adds("FloatLiteral"); hash.adds(ftoa(value)); } 77 | override string repr() { return "$value"; } 78 | } 79 | 80 | class ASTDoubleLiteral : ASTSymbol 81 | { 82 | double value; 83 | 84 | this(this.value) { } 85 | 86 | override (Symbol | Error) compile(Context context) 87 | { 88 | return new DoubleLiteral(this.value); 89 | } 90 | } 91 | 92 | class DoubleLiteral : Expression 93 | { 94 | double value; 95 | this(this.value) { this.type = once new Double; this.info = ExprInfo(Lifetime.permanent); } 96 | override int emit(Generator output) { return output.fun.doubleLiteral(this.value); } 97 | override void hash(Hash hash) { hash.adds("DoubleLiteral"); hash.adds(ftoa(value)); } 98 | override string repr() { return "$value"; } 99 | } 100 | -------------------------------------------------------------------------------- /src/neat/hash.nt: -------------------------------------------------------------------------------- 1 | module neat.hash; 2 | 3 | macro import package(compiler).std.macro.once; 4 | macro import package(compiler).std.macro.quasiquoting; 5 | 6 | import neat.array; 7 | import neat.base; 8 | import neat.float; 9 | import neat.runtime.locrange; 10 | import neat.types; 11 | import neat.util; 12 | 13 | // fallback for macros 14 | import polyhash; 15 | 16 | alias Hash = PolyHash; 17 | 18 | (Expression | Error) hash(ASTSymbol astValue, Context context, LocRange locRange) 19 | { 20 | auto value = astValue.compile(context)?.beExpression(locRange)?; 21 | Parameter[] params = [ 22 | Parameter.simple("value", value.type)]; 23 | 24 | bool isHashable = !!value.type.instanceOf(Hashable); 25 | bool isArray = !!value.type.instanceOf(Array); 26 | bool isInt = value.type.instanceOf(Integer) || value.type.instanceOf(Long) || value.type.instanceOf(Character); 27 | bool isFloat = !!value.type.instanceOf(Float); 28 | if (!isArray && !isInt && !isFloat && !isHashable) { 29 | return locRange.fail("TODO: __hash() of $(value.type.repr)"); 30 | } 31 | // TODO proper unsigned types 32 | ASTSymbol hashfn() { 33 | if (isArray) { 34 | auto array = value.type.instanceOf(Array)? else die; 35 | if (array.elementType.instanceOf(Character)) { 36 | return context.compiler.$expr ({ 37 | mut int hash = 0x811c9dc5; 38 | for (i in 0 .. value.length / 4) { 39 | int value = (cast(int*) value.ptr)[i]; 40 | hash ^= value; 41 | hash *= 0x01000193; 42 | } 43 | for (i in (value.length / 4) * 4 .. value.length) { 44 | hash ^= value.ptr[i]; 45 | hash *= 0x01000193; 46 | } 47 | if (hash < 0) hash = -hash; 48 | hash; 49 | }); 50 | } 51 | return context.compiler.$expr ({ 52 | // fnv hash 53 | mut int hash = 0x811c9dc5; 54 | for (auto element in value) { 55 | hash ^= __hash(element); 56 | hash *= 0x01000193; 57 | } 58 | if (hash < 0) hash = -hash; 59 | hash; 60 | }); 61 | } 62 | if (isInt) { 63 | // knuth hash 64 | return context.compiler.$expr ({ 65 | mut int hash = value * cast(int) 2654435761; 66 | if (hash < 0) hash = -hash; 67 | hash; 68 | }); 69 | } 70 | if (isFloat) { 71 | // knuth hash via bitcast 72 | return context.compiler.$expr ({ 73 | mut auto value = value; 74 | mut int hash = *cast(int*) &value * cast(int) 2654435761; 75 | if (hash < 0) hash = -hash; 76 | hash; 77 | }); 78 | } 79 | if (isHashable) { 80 | return value.type.instanceOf(Hashable) 81 | .hash(context.compiler, context.compiler.astIdentifier("value")) 82 | .case(Error err: exitWithError(err)); 83 | } 84 | locRange.hardCheck(false, "internal error"); 85 | } 86 | ASTStatement body_() { 87 | return context.compiler.( 88 | astExpressionStmt(astReturn(hashfn))); 89 | } 90 | 91 | // TODO unsigned 92 | auto fn = context.compiler.createRuntimeFunction( 93 | context, "__hash", once new Integer, params, &body_); 94 | 95 | return context.compiler.mkCall(fn, [value]); 96 | } 97 | -------------------------------------------------------------------------------- /src/neat/macrocache.nt: -------------------------------------------------------------------------------- 1 | /** 2 | * Allow saving and loading macro state; 3 | * or rather, the required information to reconstruct a macro state. 4 | */ 5 | module neat.macrocache; 6 | 7 | macro import package(compiler).std.macro.listcomprehension; 8 | 9 | import neat.base; 10 | import package(compiler).std.file; 11 | import package(compiler).std.json; 12 | import package(compiler).std.json.stream; 13 | import package(compiler).std.sha256; 14 | import package(compiler).std.stream; 15 | import package(compiler).std.string; 16 | import polyhash; 17 | 18 | private alias CompilerError = Error; 19 | 20 | (void | CompilerError) saveMacroCache(string importDesc, string[string] bom, LibraryCallRecord[] records) { 21 | auto target = importDesc.targetFile; 22 | auto tmp = importDesc.tmpFile; 23 | auto file = fileSink(tmp).toCompilerError?; 24 | mut (string key, JSONValue value)[] transformBom; 25 | for (key in bom.keys) transformBom ~= (key, JSONValue(bom[key])); 26 | auto dto = MacroCacheDto( 27 | fileHashes=JSONValue(transformBom), 28 | records=[RecordDto(soname=a.soname, fnname=a.fnname) for a in records], 29 | compilerHash=compilerHashStr, 30 | ); 31 | dto.encode(new JsonPrinter(file)).toCompilerError?; 32 | file.close; 33 | tmp.rename(target); 34 | } 35 | 36 | (string[string] bom, LibraryCallRecord[] records | :missing | CompilerError) loadMacroCache(string importDesc) { 37 | auto target = importDesc.targetFile; 38 | if (!target.exists) return :missing; 39 | auto file = new FileSource(target); 40 | auto source = new JsonLexer(file); 41 | auto dto = source.decode!MacroCacheDto.toCompilerError?; 42 | // cached from older compiler version 43 | if (dto.compilerHash != compilerHashStr) return :missing; 44 | mut string[string] bom; 45 | for (value in dto.fileHashes.object) { 46 | auto file = value.key; 47 | // file removed 48 | if (!file.exists) return :missing; 49 | auto currentFile = file.readText; 50 | auto digest = new Sha256; 51 | digest.update(cast(ubyte[]) currentFile); 52 | auto hexstr = digest.finalize.toHexString; 53 | // hash changed 54 | if (value.value.str != hexstr) return :missing; 55 | // unchanged from cache. 56 | bom[file] = value.value.str; 57 | } 58 | return (bom=bom, records=[(soname=a.soname, fnname=a.fnname) for a in dto.records]); 59 | } 60 | 61 | private auto toCompilerError(T)(T value) { 62 | import package(compiler).std.error : Error; 63 | alias transform = a => LocRange(a.fileId, a.from, a.to); 64 | 65 | return value.case(Error err: new CompilerError([err.range.transform], err.message)); 66 | } 67 | 68 | private string compilerHashStr() { 69 | auto hash = new Hash; 70 | hash.apply(compiler_hash_add, compiler_hash_mult); 71 | return hash.text; 72 | } 73 | 74 | private string targetFile(string importDesc) => ".obj/macrocache_$(importDesc.filter)"; 75 | 76 | private string tmpFile(string importDesc) => ".obj/.macrocache_$(importDesc.filter).tmp"; 77 | 78 | private string filter(string desc) => desc.replace(" ", "_").replace("/", "_").replace("(", "_").replace(")", "_"); 79 | 80 | struct MacroCacheDto { 81 | // TODO 82 | // string[string] fileHashes; 83 | JSONValue fileHashes; 84 | RecordDto[] records; 85 | string compilerHash; 86 | 87 | } 88 | 89 | struct RecordDto { 90 | string soname; 91 | string fnname; 92 | } 93 | 94 | private extern(C) long compiler_hash_add(); 95 | private extern(C) long compiler_hash_mult(); 96 | -------------------------------------------------------------------------------- /src/neat/pragmas.nt: -------------------------------------------------------------------------------- 1 | module neat.pragmas; 2 | 3 | import helpers; 4 | import neat.base; 5 | import neat.expr; 6 | import neat.runtime : assert; 7 | import neat.types; 8 | import polyhash; 9 | 10 | (nullable ASTPragma | Error) parsePragma(Parser parser, LexicalContext lexicalContext) 11 | { 12 | if (!parser.acceptIdentifier("pragma")) 13 | return null; 14 | parser.expectToken(TokenType.lparen)?; 15 | auto from = parser.from; 16 | auto name = parser.parseIdentifier; 17 | auto locRange = parser.to(from); 18 | mut ASTSymbol[] args; 19 | while (parser.acceptToken(TokenType.comma)) { 20 | if (auto arg = lexicalContext.compiler.parseExpression(parser, lexicalContext)?) 21 | args ~= arg; 22 | else return parser.fail("Expected expression argument"); 23 | } 24 | parser.expectToken(TokenType.rparen)?; 25 | parser.expectToken(TokenType.semicolon)?; 26 | return new ASTPragma(name, args, locRange); 27 | } 28 | 29 | class ASTPragma : ASTPragmaBase 30 | { 31 | string name; 32 | 33 | ASTSymbol[] args; 34 | 35 | LocRange locRange; 36 | 37 | this(this.name, this.args, this.locRange=__CALLER__) { } 38 | 39 | override (FinishedSymbol | Error) compile(Context context) { 40 | if (this.name == "lib") { 41 | if (this.args.length != 1) 42 | return this.locRange.fail("Unexpected arguments for pragma(lib)"); 43 | auto asStr = this.args[0].instanceOf(ASTStringLiteral); 44 | if (!asStr) return this.args[0].locRange.fail("Expected string as argument for pragma(lib)"); 45 | return new PragmaLibHint(asStr.text); 46 | } 47 | if (this.name == "framework") { 48 | if (this.args.length != 1) 49 | return this.locRange.fail("Unexpected arguments for pragma(framework)"); 50 | auto asStr = this.args[0].instanceOf(ASTStringLiteral); 51 | if (!asStr) return this.args[0].locRange.fail("Expected string as argument for pragma(framework)"); 52 | return new PragmaFrameworkHint(asStr.text); 53 | } 54 | // Note: We don't skip unknown pragmas, because we don't 55 | // care about cross-compiler compat. (what other compilers?) 56 | return this.locRange.fail("Unknown pragma '$name'"); 57 | } 58 | } 59 | 60 | class PragmaLibHint : FinishedSymbol 61 | { 62 | string library; 63 | 64 | this(this.library) { } 65 | 66 | override void emit(Generator generator) { 67 | generator.addFlag("-l$(this.library)"); 68 | } 69 | 70 | override void hash(Hash hash) { 71 | hash.adds("pragma_lib"); 72 | hash.adds(this.library); 73 | } 74 | } 75 | 76 | class PragmaFrameworkHint : FinishedSymbol 77 | { 78 | string framework; 79 | 80 | this(this.framework) { } 81 | 82 | override void emit(Generator generator) { 83 | generator.addFlag("-framework $framework"); 84 | } 85 | 86 | override void hash(Hash hash) { 87 | hash.adds("pragma_framework"); 88 | hash.adds(this.framework); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/neat/runtime.nt: -------------------------------------------------------------------------------- 1 | module neat.runtime; 2 | 3 | public import package(compiler).std.stdio : print; 4 | public import neat.runtime.locrange; 5 | 6 | string itoa(int i) { 7 | import neat.runtime.stdlib : snprintf; 8 | 9 | int len = snprintf(null, 0, "%i".ptr, i); 10 | string res = new string(len + 1); 11 | 12 | return res[0 .. snprintf(res.ptr, res.length, "%i".ptr, i)]; 13 | } 14 | 15 | int atoi(string s) { 16 | import neat.runtime.stdlib : atoi, free; 17 | 18 | char* temp = toStringz(s); 19 | int res = atoi(temp); 20 | free(temp); 21 | return res; 22 | } 23 | 24 | long atol(string s) { 25 | import neat.runtime.stdlib : atoll, free; 26 | 27 | char* temp = toStringz(s); 28 | long res = atoll(temp); 29 | free(temp); 30 | return res; 31 | } 32 | 33 | string ftoa(double d) { 34 | import neat.runtime.stdlib : snprintf; 35 | 36 | int len = snprintf(null, 0, "%f".ptr, d); 37 | string res = new string(len + 1); 38 | 39 | return res[0 .. snprintf(res.ptr, res.length, "%f".ptr, d)]; 40 | } 41 | 42 | string dtoa_hex(mut double d) { 43 | import neat.runtime.stdlib : snprintf; 44 | 45 | int len = snprintf(null, 0, "%llx".ptr, *cast(long*) &d); 46 | string res = new string(len + 1); 47 | return res[0 .. snprintf(res.ptr, res.length, "%llx".ptr, *cast(long*) &d)]; 48 | } 49 | 50 | char* toStringz(string s) { 51 | import neat.runtime.stdlib : malloc, memcpy; 52 | 53 | char* ret = cast(char*) malloc(s.length + 1); 54 | memcpy(dest=ret, src=s.ptr, s.length); 55 | ret[s.length] = '\0'; 56 | return ret; 57 | } 58 | 59 | double atof(string s) { 60 | import neat.runtime.stdlib : atof, free; 61 | 62 | char *temp = toStringz(s); 63 | double res = atof(temp); 64 | free(temp); 65 | return res; 66 | } 67 | 68 | string ltoa(long l) { 69 | import neat.runtime.stdlib : snprintf; 70 | 71 | int len = snprintf(null, 0, "%lld".ptr, l); 72 | string res = new string(len + 1); 73 | 74 | return res[0 .. snprintf(res.ptr, res.length, "%lld".ptr, l)]; 75 | } 76 | 77 | string btoa(bool b) { 78 | return "true" if b else "false"; 79 | } 80 | 81 | string ctoa(char c) { 82 | return "'" ~ c ~ "'"; 83 | } 84 | 85 | template atoa(T) { 86 | string atoa(T array) { 87 | mut string result; 88 | for (i, part in array) { 89 | if (i) result ~= ", "; 90 | result ~= "$part"; 91 | } 92 | return "[$result]"; 93 | } 94 | } 95 | 96 | string ptoa(void* ptr) { 97 | import neat.runtime.stdlib : snprintf; 98 | 99 | if (!ptr) return "null"; 100 | version (mingw) { 101 | auto ptrCode = "0x%016x".ptr if sizeof(size_t) == 8 else "0x%08x".ptr; 102 | } else { 103 | // 0x18 to compensate for 0x?? 104 | auto ptrCode = "%018p".ptr if sizeof(size_t) == 8 else "%010p".ptr; 105 | } 106 | int len = snprintf(null, 0, ptrCode, ptr); 107 | string res = new string(len + 1); 108 | 109 | return res[0 .. snprintf(res.ptr, res.length, ptrCode, ptr)]; 110 | } 111 | 112 | void assert(int test) { 113 | import neat.runtime.stdlib : exit, fputs, stderr; 114 | 115 | if (!test) { 116 | fputs("Assertion failed! Aborting.\n".ptr, stderr); 117 | exit(1); 118 | } 119 | } 120 | 121 | bottom die() { 122 | import neat.runtime.stdlib : exit; 123 | 124 | print("Internal compiler error."); 125 | exit(1); 126 | } 127 | 128 | extern(C) void neat_runtime_refcount_inc(char* desc, void* ptr); 129 | extern(C) int neat_runtime_refcount_dec(char* desc, void* ptr); 130 | -------------------------------------------------------------------------------- /src/neat/runtime/array.nt: -------------------------------------------------------------------------------- 1 | module neat.runtime.array; 2 | 3 | extern(C) void neat_runtime_index_oob(size_t index); 4 | 5 | size_t checkIndex(size_t index, size_t length) { 6 | // < 0 overflows to >= length. 7 | if (index >= length) { 8 | neat_runtime_index_oob(index); 9 | } 10 | return index; 11 | } 12 | -------------------------------------------------------------------------------- /src/neat/runtime/locrange.nt: -------------------------------------------------------------------------------- 1 | module neat.runtime.locrange; 2 | 3 | struct LocRange { 4 | int fileId; 5 | (int row, int column) from; 6 | (int row, int column) to; 7 | } 8 | -------------------------------------------------------------------------------- /src/neat/runtime/stdlib.nt: -------------------------------------------------------------------------------- 1 | module neat.runtime.stdlib; 2 | 3 | extern(C) int printf(char* format, ...); 4 | extern(C) int fprintf(void* handle, char* format, ...); 5 | extern(C) int fputs(char *str, void* handle); 6 | extern(C) int snprintf(char* str, size_t size, char* format, ...); 7 | extern(C) int fflush(void* handle); 8 | version (mingw) { 9 | extern(C) void* __acrt_iob_func(int); 10 | alias stdin = __acrt_iob_func(0); 11 | alias stdout = __acrt_iob_func(1); 12 | alias stderr = __acrt_iob_func(2); 13 | } else { 14 | extern(C) void* stdin; 15 | extern(C) void* stdout; 16 | extern(C) void* stderr; 17 | } 18 | extern(C) void exit(int); 19 | extern(C) int atoi(char*); 20 | extern(C) long atoll(char*); 21 | extern(C) double atof(char*); 22 | extern(C) void* malloc(size_t); 23 | extern(C) void free(void*); 24 | extern(C) void* memcpy(void* dest, void* src, size_t n); 25 | extern(C) int memcmp(void* s1, void* s2, size_t n); 26 | extern(C) void* memset(void* s, int c, size_t n); 27 | extern(C) void neat_runtime_lock_stdout(); 28 | extern(C) void neat_runtime_unlock_stdout(); 29 | -------------------------------------------------------------------------------- /src/neat/ternary.nt: -------------------------------------------------------------------------------- 1 | module neat.ternary; 2 | 3 | macro import package(compiler).std.macro.listcomprehension; 4 | macro import package(compiler).std.macro.quasiquoting; 5 | 6 | import neat.base; 7 | import neat.bottom; 8 | import neat.either; 9 | import neat.expr; 10 | import neat.statements; 11 | import neat.util; 12 | 13 | class ASTTernaryIf : ASTSymbol 14 | { 15 | nullable ASTSymbol test; 16 | 17 | ASTSymbol then; 18 | 19 | ASTSymbol else_; 20 | 21 | this(this.test, this.then, this.else_, super) { } 22 | 23 | override (Symbol | Error) compile(Context context) { 24 | /** 25 | * - make label 26 | * - LabelIfScope 27 | * - preallocate var 28 | * if (true) { 29 | * var = then.case(:else: breakelse) 30 | * } else { 31 | * var = else_ 32 | * } 33 | * - var 34 | */ 35 | auto ifLabel = context.getLabel; 36 | auto context = context.withNamespace(new LabelIfScope(ifLabel, hasElse=true, context.namespace)); 37 | (Expression | Error) test() => this.test 38 | .case(null: return new BoolLiteral(true)) 39 | .compile(context)? 40 | .beExpressionImplCall(context, this.test.locRange)? 41 | .(truthy(context, that, this.test.locRange)?); 42 | 43 | auto test = test?; 44 | auto then = this.then.compile(context)?.beExpressionImplCall(context, this.then.locRange)?; 45 | auto else_ = this.else_.compile(context)?.beExpressionImplCall(context, this.else_.locRange)?; 46 | // won't be visible post-merge 47 | bool elseIsBottom = !!else_.type.instanceOf(Bottom); 48 | auto merger = new TypeMerger; 49 | merger.add(then, __RANGE__, context)?; 50 | merger.add(else_, __RANGE__, context)?; 51 | auto mergedType = merger.type(context)? else die; 52 | /** 53 | * uninitialized mergedType result; 54 | * if (test) { result = then; } 55 | * else { result = else_; } 56 | */ 57 | auto lifetimeThen = then if elseIsBottom else then.take(context)?; 58 | auto lifetimeElse = else_ if elseIsBottom else else_.take(context)?; 59 | auto thenConvert = expectImplicitConvertTo( 60 | context, lifetimeThen, mergedType, this.locRange)?; 61 | auto elseConvert = expectImplicitConvertTo(context, lifetimeElse, mergedType, this.locRange)?; 62 | // If else is bottom, we can just use the lifetime of `then` directly. 63 | auto lifetime = then.info.lifetime if elseIsBottom else Lifetime.gifted; 64 | auto result = new PairedTemporary(mergedType, lifetime, context.getUniqueId); 65 | auto init = new UninitializeTemporaryStatement(result); 66 | auto ifTrue = new AssignStatement(result, thenConvert, __RANGE__); 67 | auto ifFalse = new AssignStatement(result, elseConvert, __RANGE__); 68 | auto ifStmt = new IfStatement(ifLabel, test=test, then=ifTrue, else_=ifFalse, __RANGE__); 69 | return context.compiler.(wrap(sequence(init, ifStmt), result, null)); 70 | } 71 | 72 | override string repr() { 73 | if (test) return "$then if $test else $else_"; 74 | return "$then else $else_"; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/neat/traits.nt: -------------------------------------------------------------------------------- 1 | // Very, very similar to neat.pragma. (Copypaste, why can't I quit you) 2 | module neat.traits; 3 | 4 | macro import package(compiler).std.macro.listcomprehension; 5 | 6 | import helpers; 7 | import neat.base; 8 | import neat.expr; 9 | import neat.types; 10 | import neat.util; 11 | 12 | (nullable ASTTrait | Error) parseTrait(Parser parser, LexicalContext lexicalContext) 13 | { 14 | if (!parser.acceptIdentifier("__traits")) 15 | return null; 16 | parser.expectToken(TokenType.lparen)?; 17 | auto from = parser.from; 18 | auto trait = parser.parseIdentifier; 19 | auto locRange = parser.to(from); 20 | mut ASTSymbol[] args; 21 | while (parser.acceptToken(TokenType.comma)) { 22 | if (auto arg = lexicalContext.compiler.parseExpression(parser, lexicalContext)?) 23 | args ~= arg; 24 | else return parser.fail("expected expression argument"); 25 | } 26 | parser.expectToken(TokenType.rparen)?; 27 | return new ASTTrait(trait, args, locRange); 28 | } 29 | 30 | class ASTTrait : ASTSymbol 31 | { 32 | string name; 33 | 34 | ASTSymbol[] args; 35 | 36 | this(this.name, this.args, this.locRange=__CALLER__) { } 37 | 38 | override (Symbol | Error) compile(Context context) { 39 | import neat.base : assert; 40 | if (name == "hasMember") { 41 | this.locRange.assert(args.length == 2, () => "__traits(hasMember) expected two arguments")?; 42 | auto valueExpr = args[0].compile(context)?.isExpressionImplCall(context, locRange)?? 43 | else return locRange.fail("__traits(hasMember) expected expression as first argument"); 44 | auto stringLit = args[1].compile(context)?.instanceOf(StringLiteral)? 45 | else return locRange.fail("__traits(hasMember) expected string literal as second argument"); 46 | 47 | return context.compiler.boolLiteral(.hasMember( 48 | context, valueExpr, stringLit.text, locRange)?); 49 | } 50 | if (name == "versionSet") { 51 | this.locRange.assert(args.length == 1, () => "__traits(versionSet) expected one argument")?; 52 | return .versionSet( 53 | context, args[0].compile(context)?, locRange); 54 | } 55 | return this.locRange.fail("unknown trait: \"$name\""); 56 | } 57 | } 58 | 59 | (bool | Error) hasMember(Context context, Expression value, string member, LocRange locRange=__CALLER__) { 60 | return !!value.type.accessMember(context, value, member, Protection.private_, locRange)?; 61 | } 62 | 63 | (Symbol | Error) versionSet(Context context, Symbol name, LocRange locRange) { 64 | auto nameLit = name.instanceOf(StringLiteral)? 65 | else return locRange.fail("__traits(versionSet) expected string literal as argument"); 66 | bool isSet = [any a == nameLit.text for a in context.platform.versions]; 67 | 68 | return context.compiler.boolLiteral(isSet); 69 | } 70 | -------------------------------------------------------------------------------- /src/neat/unittest_.nt: -------------------------------------------------------------------------------- 1 | module neat.unittest_; 2 | 3 | import helpers; 4 | import neat.base; 5 | import neat.function_; 6 | import neat.types; 7 | import neat.util; 8 | import package(compiler).std.string : startsWith, endsWith; 9 | 10 | (nullable ASTUnitTest | Error) parseUnitTest(Parser parser, LexicalContext lexicalContext) 11 | { 12 | parser.begin; 13 | parser.strip; 14 | auto from = parser.from; 15 | if (!parser.acceptIdentifier("unittest")) { 16 | parser.revert; 17 | return null; 18 | } 19 | parser.commit; 20 | auto locRange = parser.to(from); 21 | parser.strip; 22 | auto remainingText = parser.remainingText; 23 | ASTStatement body_ = lexicalContext.compiler.parseStatement(parser, lexicalContext)?; 24 | mut auto bodyText = remainingText[0 .. $ - parser.remainingText.length]; 25 | if (bodyText.startsWith("{") && bodyText.endsWith("}")) { 26 | bodyText = bodyText[1 .. $ - 1]; 27 | } 28 | return new ASTUnitTest(body_, lexicalContext.macroState, locRange, bodyText); 29 | } 30 | 31 | class ASTUnitTest 32 | { 33 | ASTStatement body_; 34 | 35 | MacroState macroState; 36 | 37 | LocRange locRange; 38 | 39 | // The source representation block of the unittest. 40 | // Used for docgen. 41 | string bodyText; 42 | 43 | this(this.body_, this.macroState, this.locRange, this.bodyText) { } 44 | 45 | FunctionDeclaration compile(Context context) 46 | { 47 | import package(compiler).std.string : replace; 48 | 49 | auto modname = findParent!ModuleBase(context.namespace).name.replace(".", "_"); 50 | auto unittestFun = new UnittestFunction(this.locRange, modname, this.body_, this.macroState); 51 | 52 | unittestFun.parent = context.namespace; 53 | unittestFun.resetMangleCache; 54 | return unittestFun; 55 | } 56 | } 57 | 58 | class UnittestFunction : Function 59 | { 60 | string modname; 61 | 62 | this(this.locRange, this.modname, this.statement, this.macroState) 63 | { 64 | this.name = ""; 65 | this.retWIP = new Void; 66 | this.params = []; 67 | this.hasThisArg = false; 68 | this.superMagic = :none; 69 | } 70 | 71 | override string manglePrefix() 72 | { 73 | return this.parent.mangle() ~ "_unittest_" ~ modname ~ "_" ~ ltoa(locRange.from.row); 74 | } 75 | 76 | // FIXME isn't this kinda sus? 77 | override CompiledFunction mkCompiledFunction( 78 | Function fun, Statement compiledStatement, FunctionScope stackframe, Statement[] argAssignments) 79 | { 80 | return new CompiledUnittestFunction(fun, compiledStatement, stackframe, argAssignments); 81 | } 82 | } 83 | 84 | class CompiledUnittestFunction : CompiledFunction 85 | { 86 | } 87 | -------------------------------------------------------------------------------- /src/polyhash.nt: -------------------------------------------------------------------------------- 1 | module polyhash; 2 | 3 | alias Hash = PolyHash; 4 | 5 | // Polynomial hash for composability 6 | final class PolyHash 7 | { 8 | mut long add, mult; 9 | this() { 10 | // hopefully copied from fnv 11 | // offset basis 12 | this.add = 14695981039346656037; 13 | this.mult = 1; 14 | } 15 | this(string s) { 16 | this.add = 14695981039346656037; 17 | this.mult = 1; 18 | adds(s); 19 | } 20 | void adds(string s) { 21 | // in a polynomial hash, we apply a string by computing h * p^(s.length) + ((s[0]*p + s[1])*p + s[2])*p... 22 | // iow, h * p^(s.length) + s[0] * p^(s.length - 1) + s[1] * p^(s.length - 2) + ... 23 | // p^(s.length) can be efficiently determined by counting along 24 | mut long resAdd = 0, resMult = 1; 25 | // INVERSE index cause we're counting up factors 26 | for (i in 0 .. s.length) { 27 | resAdd += cast(long) s[$ - 1 - i] * resMult; 28 | resMult *= PRIME; 29 | } 30 | apply(resAdd, resMult); 31 | } 32 | void addl(long l) { 33 | mut long resAdd = 0, resMult = 1; 34 | for (i in 0 .. 8) { 35 | resAdd += (l >> (8 * i)) & 0xff * resMult; 36 | resMult *= PRIME; 37 | } 38 | apply(resAdd, resMult); 39 | } 40 | void apply(long add, long mult) { 41 | this.add = this.add * mult + add; 42 | this.mult *= mult; 43 | } 44 | void applyHash(Hash other) { 45 | apply(other.add, other.mult); 46 | } 47 | string text() { 48 | mut string res; 49 | for (i in 0 .. 8) { 50 | char toHex(int i) { 51 | if (i < 10) return "0123456789"[i]; 52 | return "ABCDEF"[i - 10]; 53 | } 54 | int b = cast(int) ((add >> (8 * i)) & 0xff); 55 | res ~= toHex(b >> 4); 56 | res ~= toHex(b & 0xf); 57 | } 58 | // this makes output look fun & wavy by sometimes 59 | // having filenames be a bit shorter. 60 | while (!res.empty && res.front == '0') res = res[1 .. $]; 61 | return res; 62 | } 63 | } 64 | 65 | private alias PRIME = 1099511628211; 66 | -------------------------------------------------------------------------------- /src/std/container/binheap.nt: -------------------------------------------------------------------------------- 1 | module std.container.binheap; 2 | 3 | // TODO macro import in function, import in unittest 4 | macro import std.macro.assert; 5 | macro import std.macro.listcomprehension; 6 | 7 | public BinHeap!T binHeap(T, Heapier)(Heapier heapier) { 8 | return new BinHeapImpl!(T, Heapier)(heapier); 9 | } 10 | 11 | public interface BinHeap(T) { 12 | void insert(T t); 13 | T extract(); 14 | bool empty(); 15 | size_t length(); 16 | } 17 | 18 | class BinHeapImpl(T, Heapier) : BinHeap!T { 19 | private T mut[] backing; 20 | private Heapier heapier; 21 | 22 | this(this.heapier) { } 23 | 24 | override void insert(T t) { 25 | mut size_t index = backing.length; 26 | backing ~= t; 27 | while (bubbleUp(index)) index = index.up; 28 | } 29 | 30 | override T extract() { 31 | swap(0, backing.length - 1); 32 | auto res = backing[$ - 1]; 33 | backing = backing[0 .. $ - 1]; 34 | bubbleDown(0); 35 | return res; 36 | } 37 | 38 | override bool empty() => backing.empty; 39 | 40 | override size_t length() => backing.length; 41 | 42 | private bool heapierIndex(size_t left, size_t right) { 43 | return heapier(backing[left], backing[right]); 44 | } 45 | 46 | private void swap(size_t left, size_t right) { 47 | auto v = backing[left]; 48 | backing[left] = backing[right]; 49 | backing[right] = v; 50 | } 51 | 52 | private bool bubbleUp(size_t i) { 53 | if (i.isRoot || heapierIndex(i.up, i)) 54 | return false; 55 | swap(i.up, i); 56 | return true; 57 | } 58 | 59 | private void bubbleDown(size_t index) { 60 | bool leftViolated = index.left < backing.length 61 | && heapierIndex(index.left, index); 62 | bool rightViolated = index.right < backing.length 63 | && heapierIndex(index.right, index); 64 | if (!leftViolated && !rightViolated) { 65 | return; 66 | } else if (leftViolated && !rightViolated) { 67 | swap(index, index.left); 68 | bubbleDown(index.left); 69 | } else if (!leftViolated && rightViolated) { 70 | swap(index, index.right); 71 | bubbleDown(index.right); 72 | } else { 73 | // swap the heapier one to 'index', thus protecting the 74 | // heap condition in the less heapy branch 75 | auto leftHeapier = heapierIndex(index.left, index.right); 76 | size_t target = index.left if leftHeapier else index.right; 77 | swap(index, target); 78 | bubbleDown(target); 79 | } 80 | } 81 | } 82 | 83 | /** 84 | * 0 85 | * 1 2 86 | * 3 4 5 6 87 | * 7 8 9 88 | */ 89 | 90 | private bool isRoot(size_t i) => i == 0; 91 | 92 | private size_t up(size_t i) => (i - 1) / 2; 93 | 94 | private size_t left(size_t i) => i * 2 + 1; 95 | 96 | private size_t right(size_t i) => i * 2 + 2; 97 | 98 | unittest 99 | { 100 | assert([i.isRoot for i in 0 .. 10] 101 | == [true, false, false, false, false, false, false, false, false, false]); 102 | assert([i.up for i in 1 .. 10] 103 | == [0, 0, 1, 1, 2, 2, 3, 3, 4]); 104 | assert([i.left for i in 0 .. 4] 105 | == [1, 3, 5, 7]); 106 | assert([i.right for i in 0 .. 4] 107 | == [2, 4, 6, 8]); 108 | } 109 | -------------------------------------------------------------------------------- /src/std/error.nt: -------------------------------------------------------------------------------- 1 | module std.error; 2 | 3 | import neat.runtime.locrange; 4 | 5 | class Error __errorclass 6 | { 7 | string message; 8 | 9 | LocRange range; 10 | 11 | this(this.message, this.range=__CALLER__) {} 12 | 13 | string toString() => "Error: $message"; 14 | } 15 | -------------------------------------------------------------------------------- /src/std/http.nt: -------------------------------------------------------------------------------- 1 | module std.http; 2 | 3 | import std.file; 4 | import std.string; 5 | 6 | extern(C) char* tmpnam(char*); 7 | extern(C) void neat_runtime_system(string command); 8 | 9 | // Downloads a file. 10 | // The way this is done right now is really hacky. But meh. 11 | ubyte[] download(string url) { 12 | // yes this is shit but it's just a placeholder. 13 | auto tmp = tmpnam(null).cToString; 14 | 15 | neat_runtime_system("curl -s " ~ url ~ " -o " ~ tmp); 16 | return readFile(tmp); 17 | } 18 | -------------------------------------------------------------------------------- /src/std/json/util.nt: -------------------------------------------------------------------------------- 1 | module std.json.util; 2 | 3 | import neat.base; 4 | import neat.either; 5 | import neat.util : SymbolIdentifierType; 6 | 7 | /** 8 | * Easymacros can't call functions from the same module 9 | * (yet? ever? There seem some fundamental difficulties here...) 10 | * so put `(int | :none)` detection here as a helper. 11 | */ 12 | nullable Type isThisOrNoneType(Type type) { 13 | auto eitherType = type.instanceOf(Either)? else return null; 14 | auto types = eitherType.types; 15 | if (types.length != 2) return null; 16 | if (types[0].same(new SymbolIdentifierType("none"))) { 17 | return types[1]; 18 | } 19 | if (types[1].same(new SymbolIdentifierType("none"))) { 20 | return types[0]; 21 | } 22 | return null; 23 | } 24 | -------------------------------------------------------------------------------- /src/std/macro/hash.nt: -------------------------------------------------------------------------------- 1 | module std.macro.hash; 2 | 3 | macro import std.macro.quasiquoting; 4 | 5 | import neat.runtime : assert; 6 | import package(compiler).neat.base; 7 | import package(compiler).neat.util; 8 | import package(compiler).polyhash; 9 | 10 | /** 11 | * This is almost certainly too clever by half. 12 | */ 13 | class StringHashMacro : Macro 14 | { 15 | nullable Type hashType; 16 | 17 | this() { } 18 | 19 | override void apply(MacroArgs args) { 20 | auto callMacroArgs = args.instanceOf(CallMacroArgs); 21 | if (!callMacroArgs) return; 22 | callMacroArgs.transformed = applyActual(callMacroArgs? else die); 23 | } 24 | 25 | (nullable Expression | Error) applyActual(CallMacroArgs callMacroArgs) { 26 | auto context = callMacroArgs.context; 27 | auto astMember = callMacroArgs.target.instanceOf(ASTMemberBase); 28 | // TODO 29 | // if (!astMember || astMember.member != "adds") return null; 30 | 31 | // auto isHashMethod = callMacroArgs.target.compile(context).same( 32 | // (compiler.$expr (new Hash).adds).compile(context)); 33 | // TODO LateSymbol this 34 | /* 35 | auto classMethodPtr = callMacroArgs.target.compile(context)?.instanceOf(ClassMethodPtr); 36 | if (!classMethodPtr) return null; 37 | auto type = classMethodPtr.thisValue.type; 38 | // TODO look up Hash with fqn path 39 | if (!this.hashType) { 40 | auto hashType = findParent!ModuleBase(context.namespace).lookup( 41 | "Hash", context, LookupReason.identifier)?; 42 | if (!hashType || !hashType.instanceOf(Type)) return null; 43 | this.hashType = hashType.instanceOf(Type); 44 | } 45 | Type notNullHashType() { 46 | if (auto type = this.hashType) return type; 47 | assert(false); 48 | } 49 | auto isHash = type.same(notNullHashType); 50 | if (!isHash) return null; 51 | // it's a Hash.adds() call. 52 | assert(callMacroArgs.args.length == 1); 53 | auto str = callMacroArgs.args[0].sym.compile(context)?.instanceOf(StringLiteralBase); 54 | if (!str) return null; // variable call 55 | return optimizeStringCall(astMember.base, str.text, context, callMacroArgs.locRange); 56 | */ 57 | return null; 58 | } 59 | 60 | (Expression | Error) optimizeStringCall(ASTSymbol base, string str, Context context, LocRange locRange) { 61 | auto state = new PolyHash(str); 62 | auto add = context.compiler.astNumberLiteral(state.add, locRange); 63 | auto mult = context.compiler.astNumberLiteral(state.mult, locRange); 64 | 65 | if (auto expr = (context.compiler.$expr $base.apply($add, $mult)).compile(context)?.instanceOf(Expression)) 66 | return expr; 67 | assert(false); 68 | } 69 | } 70 | 71 | void addStringHashMacro(MacroState macroState) 72 | { 73 | macroState.addMacro(new StringHashMacro); 74 | } 75 | 76 | macro(addStringHashMacro); 77 | 78 | -------------------------------------------------------------------------------- /src/std/macro/once.nt: -------------------------------------------------------------------------------- 1 | module std.macro.once; 2 | 3 | macro import std.macro.quasiquoting; 4 | 5 | import neat.runtime : assert; 6 | import package(compiler).neat.base; 7 | import package(compiler).neat.util; 8 | import package(compiler).neat.types; 9 | import package(compiler).polyhash; 10 | 11 | class ASTOnceExpression : ASTSymbol 12 | { 13 | ASTSymbol target; 14 | 15 | this(this.target, this.locRange=__CALLER__) { } 16 | 17 | override (Symbol | Error) compile(Context context) 18 | { 19 | auto varName = "once__" ~ this.locRange.toString.cleanup; 20 | auto target = this.target.compile(context)?.beExpressionImplCall(context, target.locRange)?; 21 | auto initStmt = new InitGlobalVarOnce(varName, target); 22 | auto globVar = new ExternCVariable(target.type, varName, threadlocal=true); 23 | return context.compiler.statementExpression(initStmt, globVar); 24 | } 25 | } 26 | 27 | class InitGlobalVarOnce : Statement 28 | { 29 | string name; 30 | 31 | Expression value; 32 | 33 | this(this.name, this.value) {} 34 | 35 | override void emit(Generator output) { 36 | auto backendType = this.value.type.emit(output.platform); 37 | auto globalPtr = output.fun.globalVar(this.name, backendType, define=true, threadlocal=true); 38 | auto global = output.fun.load(backendType, globalPtr); 39 | auto zero = output.fun.zeroLiteral(backendType); 40 | auto eq = output.fun.binop("==", backendType, global, zero); 41 | 42 | string label = output.fun.getLabel; 43 | output.fun.testBranch(eq, label ~ "_then", label ~ "_fin"); 44 | 45 | output.fun.setLabel(label ~ "_then"); 46 | auto value = this.value.emit(output); 47 | output.fun.store(backendType, globalPtr, value); 48 | 49 | output.fun.branch(label ~ "_fin"); 50 | output.fun.setLabel(label ~ "_fin"); 51 | } 52 | 53 | override void hash(Hash hash) { 54 | hash.adds("init global"); 55 | hash.adds(name); 56 | value.hash(hash); 57 | } 58 | 59 | override string repr() { return "InitGlobalVar($name)"; } 60 | } 61 | 62 | bool isAlnum(int ch) { 63 | return isAlpha(ch) || isDigit(ch); 64 | } 65 | 66 | bool isAlpha(int ch) { 67 | return ch >= cast(int) 'a' && ch <= cast(int) 'z' 68 | || ch >= 'A' && ch <= cast(int) 'Z'; 69 | } 70 | 71 | bool isDigit(int ch) { 72 | return ch >= cast(int) '0' && ch <= cast(int) '9'; 73 | } 74 | 75 | string cleanup(string s) { 76 | mut string ret; 77 | for (ch in s) 78 | if (ch.isAlnum) ret ~= ch; 79 | else ret ~= "_"; 80 | return ret; 81 | } 82 | 83 | class OnceMacro : Macro 84 | { 85 | this() { } 86 | 87 | override void apply(MacroArgs args) { 88 | if (auto args = args.instanceOf(ParseExpressionBaseArgs)) { 89 | args.symbol = this.parse(args.parser, args.lexicalContext); 90 | } 91 | } 92 | 93 | (nullable ASTSymbol | Error) parse(Parser parser, LexicalContext lexicalContext) 94 | { 95 | parser.begin; 96 | auto from = parser.from; 97 | if (!parser.acceptIdentifier("once")) { 98 | parser.revert; 99 | return null; 100 | } 101 | parser.commit; 102 | auto locRange = parser.to(from); 103 | if (auto expression = lexicalContext.compiler.parseExpression(parser, lexicalContext)?) { 104 | return new ASTOnceExpression(expression, locRange); 105 | } else { 106 | return parser.fail("expression expected"); 107 | } 108 | } 109 | } 110 | 111 | void addOnceMacro(MacroState macroState) 112 | { 113 | macroState.addMacro(new OnceMacro); 114 | } 115 | 116 | macro(addOnceMacro); 117 | -------------------------------------------------------------------------------- /src/std/macro/quasiquoting.nt: -------------------------------------------------------------------------------- 1 | module std.macro.quasiquoting; 2 | 3 | import package(compiler).neat.base; 4 | import package(compiler).neat.quasiquoting; 5 | import package(compiler).neat.runtime; 6 | 7 | class QuasiQuoting : Macro 8 | { 9 | this() { } 10 | override void apply(MacroArgs args) { 11 | if (auto args = args.instanceOf(ParsePropertyArgs)) { 12 | args.result = this.parseProperty(args.parser, args.lexicalContext, args.left); 13 | } 14 | } 15 | 16 | (nullable ASTSymbol | Error) parseProperty(Parser parser, LexicalContext lexicalContext, ASTSymbol compilerExpr) { 17 | auto compiler = lexicalContext.compiler; 18 | 19 | parser.pinned = true; 20 | parser.begin; 21 | // .$expr 22 | if (!parser.acceptToken(TokenType.dot) || !parser.acceptToken(TokenType.dollar)) { 23 | parser.revert; 24 | return null; 25 | } 26 | auto ident = parser.parseIdentifier; 27 | if (ident != "stmt" && ident != "expr" && ident != "type") { 28 | parser.revert; 29 | return null; 30 | } 31 | parser.commit; 32 | auto from = parser.from; 33 | auto remainingText = parser.remainingText; 34 | // deadbeef scopeId, because we should not be doing anything with these results! 35 | // If you see this value, somehow a parsed quote from this block leaked. 36 | // Remember, the point of parsing here is just to determine the text fragment to 37 | // be parsed at runtime. 38 | auto quoteLexicalContext = new QuoteLexicalContext(scopeId=0xdeadbeef, compiler, lexicalContext.pak); 39 | mut string methodName; 40 | if (ident == "stmt") { 41 | auto stmt = compiler.parseStatement(parser, quoteLexicalContext)?; 42 | parser.assert_(!!stmt, "statement expected")?; 43 | methodName = "astQuotedStatement"; 44 | } else if (ident == "expr" ) { 45 | auto expr = compiler.parseExpression(parser, quoteLexicalContext)?; 46 | parser.assert_(!!expr, "expression expected")?; 47 | methodName = "astQuotedExpression"; 48 | } else if (ident == "type") { 49 | auto type = compiler.parseType(parser, quoteLexicalContext)?; 50 | parser.assert_(!!type, "type expected")?; 51 | methodName = "astQuotedType"; 52 | } else assert(false); 53 | auto locRange = parser.to(from); 54 | auto text = remainingText[0 .. $ - parser.remainingText.length]; 55 | with (compiler) { 56 | // `compiler.$stmt { $stmt a; }` 57 | // compiles to `compiler.astQuotedStatement(id, "{ $stmt a; }", pak, [], [a], [], (LocRange at '{'))` 58 | return astCall( 59 | astMember(compilerExpr, astIdentifier(methodName)), 60 | [ 61 | astStringLiteral(text), 62 | lexicalContext.pak.quote(compiler), 63 | astArrayLiteral(quoteLexicalContext.macroSymbols), 64 | astArrayLiteral(quoteLexicalContext.macroStatements), 65 | astArrayLiteral(quoteLexicalContext.macroIdentifiers), 66 | locRange.quote(compiler, compilerExpr), 67 | ], 68 | locRange); 69 | } 70 | } 71 | } 72 | 73 | void addQuasiQuotingMacro(MacroState macroState) 74 | { 75 | macroState.addMacro(new QuasiQuoting); 76 | } 77 | 78 | macro(addQuasiQuotingMacro); 79 | -------------------------------------------------------------------------------- /src/std/macro/the.nt: -------------------------------------------------------------------------------- 1 | module std.macro.the; 2 | 3 | import package(compiler).neat.base; 4 | import package(compiler).neat.function_; 5 | import package(compiler).neat.parser; 6 | import package(compiler).neat.runtime; 7 | import package(compiler).neat.util; 8 | 9 | class ASTTheValue : ASTSymbol 10 | { 11 | ASTSymbol type; 12 | 13 | this(this.type, this.locRange=__CALLER__) { } 14 | 15 | override (Expression | Error) compile(Context context) 16 | { 17 | auto type = this.type.compile(context)?.beType(this.locRange)?; 18 | mut nullable Namespace namespace = context.namespace; 19 | mut string[] checked; 20 | while (namespace) { 21 | auto varDecl = namespace.instanceOf(VariableDeclaration); 22 | if (varDecl) { 23 | if (varDecl.name == "") { 24 | auto member = varDecl.access(context); 25 | 26 | if (member.type.same(type)) 27 | return member; 28 | checked ~= member.type.repr; 29 | } 30 | namespace = namespace.parent; 31 | } 32 | else { 33 | namespace = namespace.parent; 34 | } 35 | } 36 | this.locRange.assert(false, "Type not found: $(type.repr) among $checked")?; 37 | } 38 | } 39 | 40 | class TheValue : Macro 41 | { 42 | this() { } 43 | override void apply(MacroArgs args) { 44 | auto args = args.instanceOf(ParseExpressionBaseArgs); 45 | if (args) { 46 | args.symbol = this.parse(args.parser, args.lexicalContext); 47 | } 48 | } 49 | 50 | (nullable ASTSymbol | Error) parse(Parser parser, LexicalContext context) 51 | { 52 | parser.begin; 53 | auto from = parser.from; 54 | if (!parser.acceptIdentifier("the")){ 55 | parser.revert; 56 | return null; 57 | } 58 | parser.commit; 59 | ASTSymbol type = context.compiler.parseType(parser, context)?? else die; 60 | return new ASTTheValue(type, parser.to(from)); 61 | } 62 | } 63 | 64 | void addTheValueMacro(MacroState macroState) 65 | { 66 | macroState.addMacro(new TheValue); 67 | } 68 | 69 | macro(addTheValueMacro); 70 | -------------------------------------------------------------------------------- /src/std/math/vector.nt: -------------------------------------------------------------------------------- 1 | module std.math.vector; 2 | 3 | macro import std.macro.assert; 4 | 5 | import std.math; 6 | 7 | // TODO generate documentation for aliases 8 | alias vec2f = Vector(float, 2); 9 | alias vec3f = Vector(float, 3); 10 | alias vec4f = Vector(float, 4); 11 | 12 | /// Compute the cross product of two vectors. 13 | vec3f cross(vec3f a, vec3f b) { 14 | return a.yzx * b.zxy - a.zxy * b.yzx; 15 | } 16 | 17 | /// Return a normalized vector; that is, a vector with length 1. 18 | vec3f normal(vec3f v) { 19 | return v / v.length; 20 | } 21 | 22 | unittest { 23 | assert(vec3f(2, 0, 0).normal == vec3f(1, 0, 0)); 24 | } 25 | 26 | /// Return the length of the vector. 27 | float length(vec2f v) { 28 | vec2f v2 = v * v; 29 | return sqrt(v2.x + v2.y); 30 | } 31 | 32 | /// Return the length of the vector. 33 | float length(vec3f v) { 34 | vec3f v2 = v * v; 35 | return sqrt(v2.x + v2.y + v2.z); 36 | } 37 | 38 | /// Return the angle between two vectors in radians. 39 | float angle(vec3f a, vec3f b) { 40 | auto prod = a * b; 41 | return acos(prod.x + prod.y + prod.z); 42 | } 43 | -------------------------------------------------------------------------------- /src/std/process.nt: -------------------------------------------------------------------------------- 1 | module std.process; 2 | 3 | import std.string; 4 | 5 | extern(C) char* getenv(char* name); 6 | 7 | /// The value of the environment variable named `name`. 8 | string getEnv(string name) { 9 | return name.toStringz.getenv.cToString; 10 | } 11 | 12 | private extern(C) char* tmpnam(char*); 13 | private extern(C) void neat_runtime_system(string command); 14 | private extern(C) int neat_runtime_system_iret(string command); 15 | 16 | /// Executes the shell command and returns the output it wrote. 17 | string readback(string command) { 18 | import std.file : readText, remove; 19 | import std.string : toStringz; 20 | 21 | // meh. 22 | auto tmp = tmpnam(null).cToString; 23 | neat_runtime_system(command ~ " > " ~ tmp); 24 | auto ret = readText(tmp); 25 | remove(tmp); 26 | return ret; 27 | } 28 | 29 | /// Executes the shell command. 30 | void system(string command) { 31 | neat_runtime_system(command); 32 | } 33 | 34 | // FIXME overloading 35 | int system_iret(string command) { 36 | return neat_runtime_system_iret(command); 37 | } 38 | 39 | /** 40 | * Gets the full path of the binary of the current process. 41 | */ 42 | string currentProcessPath() { 43 | import std.file : realpath; 44 | version (windows) { 45 | import c.windows : GetModuleFileName, MAX_PATH; 46 | import c.stdlib : exit; 47 | auto buffer = new char[](MAX_PATH); 48 | auto len = GetModuleFileName(null, buffer.ptr, cast(int) buffer.length); 49 | if (len == MAX_PATH) { 50 | print("current module path exceeds MAX_PATH!"); 51 | exit(1); 52 | } 53 | return buffer[0 .. len]; 54 | } else { 55 | return "/proc/self/exe".realpath; 56 | } 57 | } 58 | 59 | /** 60 | * Executes the binary found at `path` with `args`. 61 | * Blocks until the binary returns. 62 | */ 63 | int exec(string path, string[] args) { 64 | import neat.runtime.stdlib : fprintf, stderr; 65 | 66 | version (windows) { 67 | import c.windows : STARTUPINFO, PROCESS_INFORMATION, CreateProcess, WaitForSingleObject, INFINITE, CloseHandle; 68 | 69 | mut string cmdline = path; 70 | for (arg in args) cmdline ~= arg; 71 | auto cmdlinePtr = cmdline.toStringz; 72 | mut STARTUPINFO info; 73 | mut PROCESS_INFORMATION processInfo; 74 | if (!CreateProcess(null, cmdlinePtr, null, null, true, 0, null, null, &info, &processInfo)) { 75 | fprintf(stderr, "CreateProcess() failed\n"); 76 | return 1; 77 | } 78 | WaitForSingleObject(processInfo.hProcess, INFINITE); 79 | CloseHandle(processInfo.hProcess); 80 | CloseHandle(processInfo.hThread); 81 | return 0; 82 | } else { 83 | int ret = fork(); 84 | if (ret != 0) { 85 | mut int wstatus; 86 | int ret = waitpid(ret, &wstatus, 0); 87 | if (ret == -1) { 88 | fprintf(stderr, "waitpid() failed: %s\n", strerror(neat_runtime_errno)); 89 | } 90 | return wstatus; 91 | } 92 | char* pathC = toStringz(path); 93 | auto argsArray = new char*[](args.length + 1); 94 | char** argsC = argsArray.ptr; 95 | argsC[0] = pathC; 96 | for (i, arg in args) { 97 | argsC[i + 1] = arg.toStringz; 98 | } 99 | argsC[args.length + 1] = null; 100 | execv(pathC, argsC); 101 | return 0; 102 | } 103 | } 104 | 105 | private extern(C) int fork(); 106 | private extern(C) bool neat_runtime_waitpid(int); 107 | private extern(C) int execv(char* pathname, char** argv); 108 | private extern(C) int waitpid(int pid, int* wstatus, int options); 109 | private extern(C) char* strerror(int); 110 | private extern(C) int neat_runtime_errno(); 111 | -------------------------------------------------------------------------------- /src/std/range/iota.nt: -------------------------------------------------------------------------------- 1 | module std.range.iota; 2 | 3 | struct Iota 4 | { 5 | size_t from, to; 6 | bool empty() { return from == to; } 7 | size_t front() { return from; } 8 | Iota next() { return Iota(from + 1, to); } 9 | size_t length() { return to - from; } 10 | } 11 | 12 | struct IntIota 13 | { 14 | int from, to; 15 | bool empty() { return from == to; } 16 | int front() { return from; } 17 | IntIota next() { return IntIota(from + 1, to); } 18 | int length() { return to - from; } 19 | } 20 | -------------------------------------------------------------------------------- /src/std/stdio.nt: -------------------------------------------------------------------------------- 1 | module std.stdio; 2 | 3 | public import neat.runtime.stdlib : stdin, stdout, stderr; 4 | 5 | /** 6 | * Print a string to standard output. 7 | */ 8 | void print(string str) { 9 | import neat.runtime.stdlib : fflush, printf, 10 | neat_runtime_lock_stdout, neat_runtime_unlock_stdout; 11 | 12 | neat_runtime_lock_stdout; 13 | printf("%.*s\n".ptr, cast(int) str.length, str.ptr); 14 | fflush(stdout); 15 | neat_runtime_unlock_stdout; 16 | } 17 | 18 | // TODO 19 | version (linux) { 20 | public LineReader byLine(void* fileHandle) { 21 | import neat.runtime.stdlib : free; 22 | mut char *lineptr; 23 | mut size_t n; 24 | auto len = getline(&lineptr, &n, fileHandle); 25 | if (len == -1) { 26 | free(lineptr); 27 | return LineReader(fileHandle, empty=true, front=null); 28 | } 29 | // I see no good reason why you'd ever want the newline character in there. 30 | auto adjLen = 0 if len == 0 31 | else len - 2 if len > 1 && lineptr[len - 2 .. len] == "\r\n" 32 | else len - 1 if lineptr[len - 1 .. len] == "\n" 33 | else len; 34 | // TODO auto bufCopy = lineptr[0 .. n].dup; 35 | auto bufCopy = new char mut[](adjLen); 36 | for (i in 0 .. adjLen) bufCopy[i] = lineptr[i]; 37 | free(lineptr); 38 | return LineReader(fileHandle, empty=false, front=bufCopy.freeze); 39 | } 40 | 41 | private extern(C) size_t getline(char** lineptr, size_t* n, void* stream); 42 | 43 | private struct LineReader { 44 | void* fileHandle; 45 | bool empty; 46 | string front; 47 | LineReader next() { return fileHandle.byLine; } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/std/stream.nt: -------------------------------------------------------------------------------- 1 | module std.stream; 2 | 3 | import std.error; 4 | 5 | /** 6 | * Source of a stream of `T`. This data type represents the ability to request 7 | * a succession of values. As opposed to ranges, it is not possible to back up 8 | * your position in the stream. 9 | * Furthermore, if it is a reference type, you should not hold on to the returned 10 | * value past the next call to `get()`. 11 | * Returns `:else` if the end of the stream is reached. 12 | * Returns `Error` if an error happened. 13 | */ 14 | interface Source(T) 15 | { 16 | (T | :else | Error) get(); 17 | } 18 | 19 | /** 20 | * Sink for a stream of `T`. This data type represents the ability to dispatch 21 | * a succession of values. 22 | * Returns `Error` if an error happened. 23 | */ 24 | interface Sink(T) 25 | { 26 | (void | Error) put(T); 27 | } 28 | 29 | class ArraySource(T) : Source!T 30 | { 31 | T[] array; 32 | 33 | this(T[] array) { this.array = array; } 34 | 35 | override (T | :else | Error) get() { 36 | if (array.empty) 37 | return :else; 38 | auto result = array[0]; 39 | array = array[1 .. $]; 40 | return result; 41 | } 42 | } 43 | 44 | class ArraySink(T) : Sink!T 45 | { 46 | T[] array_; 47 | 48 | this() { } 49 | 50 | public T[] array() => array_; 51 | 52 | override (void | Error) put(T value) { 53 | array_ ~= value; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /test/fail_compilation/abstract.nt: -------------------------------------------------------------------------------- 1 | module abstract; 2 | 3 | extern(C) void assert(int); 4 | 5 | abstract class A 6 | { 7 | abstract void foo() { assert(false); } 8 | } 9 | 10 | class B : A 11 | { 12 | this() { } 13 | } 14 | 15 | void main() { A a = new B; a.foo(); } 16 | -------------------------------------------------------------------------------- /test/fail_compilation/afl_1.nt: -------------------------------------------------------------------------------- 1 | module afl_1; 2 | 3 | int ack(int m,) {} 4 | -------------------------------------------------------------------------------- /test/fail_compilation/afl_2.nt: -------------------------------------------------------------------------------- 1 | module afl_2; 2 | 3 | void main() { 4 | for (bla < 10; i = 1) { } 5 | } 6 | -------------------------------------------------------------------------------- /test/fail_compilation/afl_3.nt: -------------------------------------------------------------------------------- 1 | module afl_3; 2 | 3 | void main() { 4 | &x; 5 | } 6 | -------------------------------------------------------------------------------- /test/fail_compilation/afl_4.nt: -------------------------------------------------------------------------------- 1 | module afl_4; 2 | 3 | void main() { string s = " 4 | -------------------------------------------------------------------------------- /test/fail_compilation/class_private_member.nt: -------------------------------------------------------------------------------- 1 | module class_private_member; 2 | 3 | class A { 4 | this() { } 5 | private int i; 6 | } 7 | 8 | void main() { 9 | A a = new A; 10 | int i = a.i; 11 | } 12 | -------------------------------------------------------------------------------- /test/fail_compilation/class_private_method.nt: -------------------------------------------------------------------------------- 1 | module class_private_method; 2 | 3 | class A { 4 | this() { } 5 | private int foo() { return 5; } 6 | } 7 | 8 | void main() { 9 | A a = new A; 10 | int f = a.foo; 11 | } 12 | -------------------------------------------------------------------------------- /test/fail_compilation/class_template.nt: -------------------------------------------------------------------------------- 1 | module class_template; 2 | 3 | interface Intf(T) { 4 | } 5 | 6 | class Class(T) : Intf!T { 7 | this() { } 8 | } 9 | 10 | void foo(T)(T arg, Intf!int obj) {} 11 | void main() { foo(5, new Class!float); } 12 | -------------------------------------------------------------------------------- /test/fail_compilation/contravariance.nt: -------------------------------------------------------------------------------- 1 | module contravariance; 2 | 3 | macro import std.macro.assert; 4 | 5 | class A { 6 | this() { } 7 | void set(A a) { } 8 | } 9 | 10 | class B : A { 11 | this() { } 12 | override void set(B b) { } 13 | } 14 | 15 | void main() { } 16 | -------------------------------------------------------------------------------- /test/fail_compilation/covariance.nt: -------------------------------------------------------------------------------- 1 | module covariance; 2 | 3 | macro import std.macro.assert; 4 | 5 | class A { 6 | this() { } 7 | A get() { return new A; } 8 | } 9 | 10 | class C { 11 | this() { } 12 | } 13 | 14 | class B : A { 15 | this() { } 16 | override C get() { return new C; } 17 | } 18 | 19 | void main() { } 20 | -------------------------------------------------------------------------------- /test/fail_compilation/either.nt: -------------------------------------------------------------------------------- 1 | module either; 2 | 3 | macro import std.macro.assert; 4 | 5 | void main() { 6 | (string | int) e = "foo"; 7 | assert(e.case(float f: 1) == 1); 8 | } 9 | -------------------------------------------------------------------------------- /test/fail_compilation/import_private_member.nt: -------------------------------------------------------------------------------- 1 | module import_private_member; 2 | 3 | import private_member; 4 | 5 | void main() { foo(); } 6 | -------------------------------------------------------------------------------- /test/fail_compilation/lambda_escape_class_field.nt: -------------------------------------------------------------------------------- 1 | module lambda_escape_class_field; 2 | 3 | void main() { 4 | auto lambda = a => 0; 5 | alias L = typeof(lambda); 6 | auto c = new C!L(lambda); 7 | } 8 | 9 | class C(T) { 10 | T value; 11 | this(T value) { 12 | // this should fail (field assignment to opaque container) 13 | this.value = value; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /test/fail_compilation/lambda_escape_return.nt: -------------------------------------------------------------------------------- 1 | module lambda_escape_return; 2 | 3 | auto test() { 4 | auto lambda = a => 2; 5 | auto nest() { return lambda; } 6 | // This one will error. 7 | return nest; 8 | } 9 | 10 | void main() { 11 | test()(); 12 | } 13 | -------------------------------------------------------------------------------- /test/fail_compilation/lambda_escape_return_either.nt: -------------------------------------------------------------------------------- 1 | module lambda_escape_return_either; 2 | 3 | auto test() { 4 | auto lambda = () => 2; 5 | (:a | :b) x = :a; 6 | return x.case(:a: lambda, :b: 5); 7 | } 8 | 9 | void main() { 10 | } 11 | -------------------------------------------------------------------------------- /test/fail_compilation/lambda_escape_return_tuple.nt: -------------------------------------------------------------------------------- 1 | module lambda_escape_return_tuple; 2 | 3 | auto test() { 4 | auto lambda = () => 2; 5 | return (lambda, 0); 6 | } 7 | 8 | void main() { 9 | test()[0](); 10 | } 11 | -------------------------------------------------------------------------------- /test/fail_compilation/lambda_escape_struct_return.nt: -------------------------------------------------------------------------------- 1 | module lambda_escape_struct_return; 2 | 3 | auto test() { 4 | auto lambda = a => 2; 5 | struct S { 6 | typeof(lambda) l; 7 | } 8 | auto s = S(lambda); 9 | // this will fail. 10 | return s; 11 | } 12 | 13 | void main() { 14 | test().l(); 15 | } 16 | -------------------------------------------------------------------------------- /test/fail_compilation/long_literal.nt: -------------------------------------------------------------------------------- 1 | module long_literal; 2 | 3 | void main() { 4 | int i = 1_000_000_000_000; 5 | } 6 | -------------------------------------------------------------------------------- /test/fail_compilation/missing_symbol_import.nt: -------------------------------------------------------------------------------- 1 | module missing_symbol_import; 2 | 3 | import std.stdio : does_not_exist; 4 | 5 | void main() { } 6 | -------------------------------------------------------------------------------- /test/fail_compilation/mutable.nt: -------------------------------------------------------------------------------- 1 | module mutable; 2 | 3 | void main() { 4 | int[] array; 5 | array ~= 5; 6 | } 7 | -------------------------------------------------------------------------------- /test/fail_compilation/named_arg.nt: -------------------------------------------------------------------------------- 1 | module named_arg; 2 | 3 | void foo(int i) { } 4 | 5 | void main() { 6 | foo(j=1); 7 | } 8 | -------------------------------------------------------------------------------- /test/fail_compilation/named_arg_class.nt: -------------------------------------------------------------------------------- 1 | module named_arg_class; 2 | 3 | class C { 4 | this() { } 5 | void foo(int i) { } 6 | } 7 | 8 | void main() { 9 | (new C).foo(j=1); 10 | } 11 | -------------------------------------------------------------------------------- /test/fail_compilation/named_arg_struct.nt: -------------------------------------------------------------------------------- 1 | module named_arg_struct; 2 | 3 | struct S { 4 | void foo(int i) { } 5 | } 6 | 7 | void main() { 8 | S().foo(j=1); 9 | } 10 | -------------------------------------------------------------------------------- /test/fail_compilation/named_arg_struct2.nt: -------------------------------------------------------------------------------- 1 | module named_arg_struct2; 2 | 3 | struct S { 4 | int i; 5 | } 6 | 7 | void main() { 8 | auto s = S(j=1); 9 | } 10 | -------------------------------------------------------------------------------- /test/fail_compilation/nested_struct.nt: -------------------------------------------------------------------------------- 1 | module nested_struct; 2 | 3 | void main() { 4 | int a = 5; 5 | struct S { 6 | int test() { return a; } 7 | } 8 | assert(S().test == 5); 9 | } 10 | -------------------------------------------------------------------------------- /test/fail_compilation/private_import_test.nt: -------------------------------------------------------------------------------- 1 | module private_import_test; 2 | 3 | import private_import; 4 | 5 | void main() { 6 | foo; 7 | } 8 | -------------------------------------------------------------------------------- /test/fail_compilation/silent_discard.nt: -------------------------------------------------------------------------------- 1 | module silent_discard; 2 | 3 | import std.error; 4 | macro import std.macro.assert; 5 | 6 | (void | Error) test() { 7 | return new Error("foo"); 8 | } 9 | 10 | (void | Error) test2() { 11 | test; 12 | return; 13 | } 14 | 15 | void main() { 16 | auto ret = test2; 17 | assert(ret.case(void: false, Error e: e.message == "foo")); 18 | } 19 | -------------------------------------------------------------------------------- /test/fail_compilation/struct_private_member.nt: -------------------------------------------------------------------------------- 1 | module struct_private_member; 2 | 3 | struct S { 4 | private int i; 5 | } 6 | 7 | void main() { 8 | int i = S().i; 9 | } 10 | -------------------------------------------------------------------------------- /test/fail_compilation/struct_private_method.nt: -------------------------------------------------------------------------------- 1 | module struct_private_method; 2 | 3 | struct S { 4 | private int foo() { return 5; } 5 | } 6 | 7 | void main() { 8 | int f = S().foo; 9 | } 10 | -------------------------------------------------------------------------------- /test/imports/member.nt: -------------------------------------------------------------------------------- 1 | module member; 2 | 3 | void foo() { } 4 | -------------------------------------------------------------------------------- /test/imports/private_import.nt: -------------------------------------------------------------------------------- 1 | module private_import; 2 | 3 | import member : foo; 4 | -------------------------------------------------------------------------------- /test/imports/private_member.nt: -------------------------------------------------------------------------------- 1 | module private_member; 2 | 3 | private void foo() { } 4 | -------------------------------------------------------------------------------- /test/imports/public_import.nt: -------------------------------------------------------------------------------- 1 | module public_import; 2 | 3 | public import member : foo; 4 | -------------------------------------------------------------------------------- /test/runnable/abstract.nt: -------------------------------------------------------------------------------- 1 | module abstract; 2 | 3 | extern(C) void assert(int); 4 | 5 | abstract class A 6 | { 7 | abstract void foo() { assert(false); } 8 | } 9 | 10 | class B : A 11 | { 12 | this() { } 13 | 14 | override void foo() { } 15 | } 16 | 17 | void main() { A a = new B; a.foo(); } 18 | -------------------------------------------------------------------------------- /test/runnable/ack.nt: -------------------------------------------------------------------------------- 1 | module ack; 2 | 3 | macro import std.macro.assert; 4 | import neat.runtime : itoa, print; 5 | 6 | void main() { int result = ack(3, 8); print("ack(3, 8) = " ~ itoa(result)); assert(result == 2045); } 7 | 8 | int ack(int m, int n) { 9 | // TODO , mut int n) 10 | mut int n = n; 11 | if (m == 0) { n = n + 1; return n; } 12 | if (n == 0) { int m1 = m - 1; return ack(m1, 1); } 13 | int m1 = m - 1; int n1 = n - 1; 14 | return ack(m1, ack(m, n1)); 15 | } 16 | -------------------------------------------------------------------------------- /test/runnable/algo.nt: -------------------------------------------------------------------------------- 1 | module algo; 2 | 3 | void main() { 4 | import std.algorithm : array, map; 5 | 6 | auto a = "hello"; 7 | auto b = map([a], a => a); 8 | print(b.front); 9 | } 10 | -------------------------------------------------------------------------------- /test/runnable/aliastest.nt: -------------------------------------------------------------------------------- 1 | module aliastest; 2 | 3 | macro import std.macro.assert; 4 | 5 | alias A = int; 6 | 7 | class C { 8 | A a; 9 | this(A a) { this.a = a; } 10 | alias B = int; 11 | B test() { 12 | alias C = int; 13 | C c = this.a; 14 | return c; 15 | } 16 | } 17 | 18 | struct S 19 | { 20 | int a; 21 | int b; 22 | 23 | alias c = a + b; 24 | } 25 | 26 | void main() { 27 | assert((new C(5)).test == 5); 28 | // FIXME assert(S(2, 3).c == 5); 29 | auto s = S(2, 3); 30 | assert(s.c == 5); 31 | } 32 | -------------------------------------------------------------------------------- /test/runnable/arraytest.nt: -------------------------------------------------------------------------------- 1 | module arraytest; 2 | 3 | macro import std.macro.assert; 4 | 5 | void main() { 6 | mut int[] arr; 7 | assert(arr.length == 0); 8 | // arr = [2, 3]; 9 | arr = new int[](0) ~ 2 ~ 3; 10 | assert(arr.length == 2); 11 | assert(arr[1] == 3); 12 | arr = arr ~ 4; // [2, 3, 4] 13 | assert(arr.length == 3); 14 | assert(arr[2] == 4); 15 | arr = arr ~ (new int[](0) ~ 5); // [2, 3, 4, 5] 16 | assert(arr.length == 4); 17 | assert(arr[3] == 5); 18 | assert(arr == [2, 3, 4, 5]); 19 | assert(arr[$ - 1] == 5); 20 | assert(arr[0 .. $ - 1] == [2, 3, 4]); 21 | assert(arr[1 .. 3] == [3, 4]); 22 | assert(arr.ptr[1 .. 2] == arr[1 .. 2]); 23 | assert("Hello World"["Hello ".length .. "Hello World".length] == "World"); 24 | int[] arr = []; 25 | assert(arr == []); 26 | assert(arr == null); 27 | { 28 | mut int count; 29 | int[] test() { count += 1; return [1, 2]; } 30 | assert(test()[$ - 1] == 2); 31 | assert(count == 1); 32 | } 33 | doublingTestArrElem; 34 | doublingTestArrArr; 35 | appendLoopTest; 36 | mutateDirectlyTest; 37 | mutateAliasTest; 38 | castTest; 39 | // polysemous array literal 40 | float[] arr = [2, 3]; 41 | } 42 | 43 | void doublingTestArrElem() { 44 | mut int[] a = [1, 2, 3, 4]; 45 | a ~= 5; 46 | int[] a1 = a ~ 6; 47 | int[] a2 = a ~ 7; 48 | assert(a1 == [1, 2, 3, 4, 5, 6]); 49 | assert(a2 == [1, 2, 3, 4, 5, 7]); 50 | assert(a1.ptr is a.ptr); 51 | assert(a2.ptr !is a.ptr); 52 | } 53 | 54 | void doublingTestArrArr() { 55 | mut int[] a = [1, 2, 3, 4]; 56 | a ~= [5]; 57 | int[] a1 = a ~ [6]; 58 | int[] a2 = a ~ [7]; 59 | assert(a1 == [1, 2, 3, 4, 5, 6]); 60 | assert(a2 == [1, 2, 3, 4, 5, 7]); 61 | assert(a1.ptr is a.ptr); 62 | assert(a2.ptr !is a.ptr); 63 | } 64 | 65 | void appendLoopTest() { 66 | mut size_t[] ia; 67 | for (i in 0 .. 100) ia ~= i; 68 | for (i in 0 .. 100) assert(ia[i] == i); 69 | } 70 | 71 | void mutateDirectlyTest() { 72 | mut int mut[] ia; 73 | ia ~= 4; 74 | ia[0] = 5; 75 | } 76 | 77 | void mutateAliasTest() { 78 | auto ia = new int mut[](2); 79 | alias first = ia[0]; 80 | first = 5; 81 | int i = 1; 82 | alias second = ia[i]; 83 | second = 6; 84 | assert(ia == [5, 6]); 85 | mut int[int] ih; 86 | alias third = ih[3]; 87 | third = 3; 88 | assert(ih[3] == 3); 89 | } 90 | 91 | void castTest() { 92 | char[] foo = "A"; 93 | ubyte[] bar = cast(ubyte[]) foo; 94 | assert(bar == [cast(ubyte) 65]); 95 | char[] baz = "ABCD"; 96 | int[] ble = cast(int[]) baz; 97 | assert(ble == [1145258561]); 98 | } 99 | -------------------------------------------------------------------------------- /test/runnable/autotest.nt: -------------------------------------------------------------------------------- 1 | module autotest; 2 | 3 | macro import std.macro.assert; 4 | 5 | void main() { 6 | assert(foo == 5); 7 | auto bar() { return 6; } 8 | assert(bar == 6); 9 | } 10 | 11 | auto foo() { return 5; } 12 | -------------------------------------------------------------------------------- /test/runnable/breakelse.nt: -------------------------------------------------------------------------------- 1 | module breakelse; 2 | 3 | macro import std.macro.assert; 4 | 5 | void basetest() { 6 | void check((bool | :test) value, bool ifTaken) { 7 | if (bool b = value.case(:test: breakelse)) { 8 | assert(ifTaken); 9 | } else { 10 | assert(!ifTaken); 11 | } 12 | 13 | if (bool b = value.case(:test: breakelse)) { 14 | assert(ifTaken); 15 | return; 16 | } 17 | assert(!ifTaken); 18 | } 19 | check(value=false, ifTaken=false); 20 | check(value=true, ifTaken=true); 21 | check(value=:test, ifTaken=false); 22 | } 23 | 24 | void findtest() { 25 | (size_t | :else) find(string text, string marker) { 26 | for (mut size_t i = 0; i <= text.length - marker.length; i++) { 27 | if (text[i .. i + marker.length] == marker) 28 | return i; 29 | } 30 | return :else; 31 | } 32 | 33 | if (size_t pos = "Helloworld".find("owo")?) { 34 | assert(pos == 4); 35 | } else { 36 | assert(false); 37 | } 38 | assert("Helloworld".find("owo").(that? else return) == 4); 39 | assert("Helloworld".find("uwu").(that? else return) == 4); 40 | } 41 | 42 | void main() { 43 | basetest; 44 | findtest; 45 | } 46 | -------------------------------------------------------------------------------- /test/runnable/casts.nt: -------------------------------------------------------------------------------- 1 | module casts; 2 | 3 | macro import std.macro.assert; 4 | 5 | void main() { 6 | { 7 | long l = 1; 8 | int i = cast(int) l; 9 | assert(i == 1); 10 | } 11 | { 12 | float f = 2.5f; 13 | int i = cast(int) f; 14 | assert(i == 2); 15 | } 16 | { 17 | mut int i = 5; 18 | int *ip = &i; 19 | void *vp = cast(void*) ip; 20 | int* ip2 = cast(int*) vp; 21 | assert(*ip2 == 5); 22 | *cast(int*) vp += 1; 23 | assert(*ip2 == 6); 24 | } 25 | { 26 | char ch = cast(char) 0xff; 27 | ubyte ub = cast(ubyte) 0xff; 28 | short s = cast(short) 0xffff; 29 | assert(cast(int) ch == 255); 30 | assert(cast(int) ub == 255); 31 | assert(cast(int) s == -1); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/runnable/chartest.nt: -------------------------------------------------------------------------------- 1 | module chartest; 2 | 3 | macro import std.macro.assert; 4 | 5 | void main() { 6 | assert('a' == "a"[0]); 7 | assert('\n' == "\n"[0]); 8 | assert('\xff' == 255); 9 | assert('\xA8' == 168); 10 | } 11 | -------------------------------------------------------------------------------- /test/runnable/classfntest.nt: -------------------------------------------------------------------------------- 1 | module classfntest; 2 | 3 | macro import std.macro.assert; 4 | 5 | void callme(void delegate() dg) { 6 | dg(); 7 | } 8 | 9 | class A { 10 | mut int i; 11 | this() { } 12 | void incr() { i += 1; } 13 | } 14 | 15 | void main() { 16 | auto a = new A; 17 | a.incr; 18 | assert(a.i == 1); 19 | callme(&a.incr); 20 | assert(a.i == 2); 21 | } 22 | -------------------------------------------------------------------------------- /test/runnable/classtest.nt: -------------------------------------------------------------------------------- 1 | module classtest; 2 | 3 | macro import std.macro.assert; 4 | 5 | extern(C) void print(string); 6 | 7 | void main() 8 | { 9 | Class1 class1 = new Class1; 10 | assert(class1.getValue() == 5); 11 | assert(Class1.foo == 8); 12 | Class2 class2 = new Class2(6); 13 | assert(class2.getValue() == 6); 14 | Class1 class2_as_1 = class2; // TODO new Class2(6) 15 | assert(class2_as_1 == class2); 16 | assert(class2_as_1.getValue() == 6); 17 | assert(!!class2_as_1.instanceOf(Class1)); 18 | assert(!!class2_as_1.instanceOf(Class2)); 19 | if (class2_as_1.instanceOf(Class3)) print("something is very wrong."); 20 | assert(!class2_as_1.instanceOf(Class3)); 21 | auto class3b = new Class3b(7); 22 | assert(class3b.a == 7); 23 | auto class3c = new Class3c(7, 8); 24 | assert(class3c.a == 7); 25 | assert(class3c.b == 8); 26 | test2; 27 | test3; 28 | test4; 29 | test5; 30 | assert(Class10.a == 5); 31 | } 32 | 33 | class Class1 34 | { 35 | this() { } 36 | int value() { return 5; } 37 | int getValue() { return this.value(); } 38 | static int foo() { return 8; } 39 | } 40 | 41 | class Class2 : Class1 42 | { 43 | int a; 44 | this(this.a) { } 45 | override int value() { return this.a; } 46 | } 47 | 48 | class Class3 : Class2 49 | { 50 | mut Class3 parent; 51 | this(this.a) { } 52 | override int value() { return super.value + 4; } 53 | } 54 | 55 | class Class3b : Class2 56 | { 57 | this(int a) { super(a); } 58 | } 59 | 60 | class Class3c : Class2 61 | { 62 | int b; 63 | this(super, this.b) { } 64 | } 65 | 66 | class Class4a { Class4b partner; } 67 | class Class4b { Class4a partner; } 68 | 69 | void test2() 70 | { 71 | assert((new Class3(5)).value == 9); 72 | } 73 | 74 | class Class5 { 75 | this() { } 76 | 77 | import std.string : replace; 78 | 79 | string test() { return "Hello World".replace(" ", "_"); } 80 | } 81 | 82 | void test3() 83 | { 84 | assert((new Class5).test == "Hello_World"); 85 | } 86 | 87 | abstract class Class6 { 88 | abstract int foo(); 89 | } 90 | 91 | class Class7 : Class6 { 92 | override int foo() { return 5; } 93 | this() { } 94 | } 95 | 96 | void test4() { 97 | assert((new Class7).foo == 5); 98 | } 99 | 100 | class Class8 101 | { 102 | int x; 103 | this(this.x = 5) { } 104 | } 105 | 106 | void test5() { 107 | assert((new Class8).x == 5); 108 | assert((new Class8()).x == 5); 109 | assert((new Class8(3)).x == 3); 110 | } 111 | 112 | interface Intf 113 | { 114 | void foo(); 115 | } 116 | 117 | abstract class Class9 : Intf 118 | { 119 | } 120 | 121 | class Class10 122 | { 123 | alias a = 5; 124 | } 125 | -------------------------------------------------------------------------------- /test/runnable/covariance_contravariance.nt: -------------------------------------------------------------------------------- 1 | module covariance_contravariance; 2 | 3 | macro import std.macro.assert; 4 | 5 | class A { 6 | this() { } 7 | A get() { return new A; } 8 | void set(B b) { } 9 | } 10 | 11 | class B : A { 12 | this() { } 13 | override B get() { return new B; } 14 | override void set(A a) { } 15 | } 16 | 17 | void main() { 18 | assert((new B()).get().instanceOf(A)); 19 | } 20 | -------------------------------------------------------------------------------- /test/runnable/discard.nt: -------------------------------------------------------------------------------- 1 | module discard; 2 | 3 | macro import std.macro.assert; 4 | import std.error; 5 | 6 | (void | Error) test() { 7 | return new Error("foo"); 8 | } 9 | 10 | (void | Error) test2() { 11 | test?; 12 | return; 13 | } 14 | 15 | void main() { 16 | auto ret = test2; 17 | assert(ret.case(void: false, Error e: e.message == "foo")); 18 | } 19 | -------------------------------------------------------------------------------- /test/runnable/easymacro.nt: -------------------------------------------------------------------------------- 1 | module easymacro; 2 | 3 | macro import std.macro.assert; 4 | macro import std.macro.easymacro; 5 | 6 | void main() { 7 | mut string marker; 8 | macro { 9 | import neat.array; 10 | 11 | assert(expr("marker")?.type.instanceOf(Array)); 12 | int a = 5; 13 | auto printIdentifier = compiler.astIdentifier("print"); 14 | code { 15 | import std.stdio : print; 16 | marker ~= "A"; 17 | } 18 | code { 19 | $printIdentifier("Printed during runtime."); 20 | marker ~= "B"; 21 | { 22 | auto b = 8; 23 | macro { 24 | print(" CT value a is $a"); 25 | auto astA = compiler.astNumberLiteral(a); 26 | code { 27 | auto a = $astA; 28 | assert(b == 8); 29 | print(" RT value a is $a, b = $b"); 30 | marker ~= "C"; 31 | } 32 | } 33 | } 34 | marker ~= "D"; 35 | } 36 | } 37 | assert(marker == "ABCD"); 38 | } 39 | -------------------------------------------------------------------------------- /test/runnable/enumtest.nt: -------------------------------------------------------------------------------- 1 | module enumtest; 2 | 3 | macro import std.macro.assert; 4 | 5 | enum Enum 6 | { 7 | first, 8 | second, 9 | third, 10 | } 11 | 12 | void main() { 13 | Enum a = Enum.first; 14 | assert(a == 0); 15 | assert(a == Enum.first); 16 | assert(a != Enum.second); 17 | assert(Enum.third == 2); 18 | } 19 | -------------------------------------------------------------------------------- /test/runnable/eqtest.nt: -------------------------------------------------------------------------------- 1 | module eqtest; 2 | 3 | macro import std.macro.assert; 4 | 5 | void main() { 6 | char ch = "A"[0]; 7 | assert(ch == "A"[0]); 8 | } 9 | -------------------------------------------------------------------------------- /test/runnable/floattest.nt: -------------------------------------------------------------------------------- 1 | module floattest; 2 | 3 | macro import std.macro.assert; 4 | 5 | void main() { 6 | float f = 2; 7 | assert(f + 2 == 4); 8 | assert(5 / f == 2.5); 9 | float g = 3.5; // polysemous 10 | float h = -3.5; 11 | assert(0 - 4f == -4.0f); 12 | assert(100_000f == 100_000.0f); 13 | assert(cast(int) 1234.0f == 1234); 14 | assert(cast(long) 1234.0 == 1234); 15 | } 16 | -------------------------------------------------------------------------------- /test/runnable/forlooptest.nt: -------------------------------------------------------------------------------- 1 | module forlooptest; 2 | 3 | macro import std.macro.assert; 4 | 5 | void extlooptest() { 6 | string[] array = ["foo", "bar"]; 7 | for (a in array) { assert(a == "foo"); break; } 8 | for (auto a in array) { assert(a == "foo"); break; } 9 | for (i, a in array) { assert(a == "foo" && i == 0); break; } 10 | for (size_t i, string a in array) { assert(a == "foo" && i == 0); break; } 11 | } 12 | 13 | void main() 14 | { 15 | { 16 | mut int k = 0; 17 | for (mut int i = 0; i < 10; i += 1) k += 1; 18 | assert(k == 10); 19 | for (mut int i = 10; i > 0; i -= 1) k -= 1; 20 | assert(k == 0); 21 | } 22 | { 23 | mut int k = 0; 24 | for (mut int i = 0; i < 10; i++) k++; 25 | assert(k == 10); 26 | for (mut int i = 10; i > 0; i--) k--; 27 | assert(k == 0); 28 | } 29 | { 30 | mut int k = 0; 31 | for (mut int i = 0; i < 10; ++i) ++k; 32 | assert(k == 10); 33 | for (mut int i = 10; i > 0; --i) --k; 34 | assert(k == 0); 35 | } 36 | // test: loop variable reinitialization 37 | for (mut int i = 0; i < 10; i += 1) { 38 | mut int l; 39 | assert(l == 0); 40 | l = 1; 41 | } 42 | { 43 | mut int i; 44 | for (mut int k = 0; k < 10; k += 1) { 45 | if (k < 3) continue; 46 | i += 1; 47 | if (k > 5) break; 48 | } 49 | assert(i == 4); 50 | } 51 | { 52 | mut int i; 53 | for (j in 0..10) { 54 | i += 1; 55 | } 56 | assert(i == 10); 57 | } 58 | { 59 | mut int sum; 60 | for (j in [2, 3, 4]) { 61 | sum += j; 62 | } 63 | assert(sum == 9); 64 | } 65 | 66 | extlooptest; 67 | } 68 | -------------------------------------------------------------------------------- /test/runnable/formatstrings.nt: -------------------------------------------------------------------------------- 1 | module formatstrings; 2 | 3 | macro import std.macro.assert; 4 | 5 | void main() { 6 | int i = 5; 7 | assert("i = $i" == "i = 5"); 8 | float f = 1.5f, g = 3.0f; 9 | assert("$f and $g" == "1.500000 and 3.000000"); 10 | assert("the sum is $(f + g)." == "the sum is 4.500000."); 11 | assert("$(f + g =)." == "f + g = 4.500000."); 12 | long l; 13 | assert("$(l)" == "0"); // long 14 | int[] a; 15 | assert("$(a.length)" == "0"); // size_t 16 | testTypes; 17 | } 18 | 19 | void testTypes() { 20 | assert("$(2)" == "2"); 21 | assert("$(2.0f)" == "2.000000"); 22 | assert("$(2.0)" == "2.000000"); 23 | assert("$(cast(short) 2)" == "2"); 24 | assert("$(cast(ubyte) 2)" == "2"); 25 | assert("$(true)" == "true"); 26 | assert("$([2, 3, 4])" == "[2, 3, 4]"); 27 | assert("$(A(2))" == "A(i=2)"); 28 | assert("$(null)" == "null"); 29 | assert("$(cast(void*) null)" == "null"); 30 | assert("$(cast(void*) 1)" == ("0x0000000000000001" if sizeof(size_t) == 8 else "0x00000001")); 31 | assert("$(:foo)" == ":foo"); 32 | assert("$((2, false))" == "(2, false)"); 33 | assert("$((a=2, b=false))" == "(a=2, b=false)"); 34 | assert("$(sum(false))" == "foo"); 35 | assert("$(sum(true))" == "2"); 36 | assert("$(vec2i(2, 3))" == "<2, 3>"); 37 | assert("$(Enum.A)" == "A"); 38 | assert("$(Enum.B)" == "B"); 39 | } 40 | 41 | alias vec2i = Vector(int, 2); 42 | 43 | struct A { 44 | int i; 45 | // FIXME autogen 46 | string toString() return "A(i=$i)"; 47 | } 48 | 49 | enum Enum { A, B } 50 | 51 | (int | string) sum(bool b) { 52 | if (b) return 2; 53 | else return "foo"; 54 | } 55 | -------------------------------------------------------------------------------- /test/runnable/funcptrtest.nt: -------------------------------------------------------------------------------- 1 | module funcptrtest; 2 | 3 | macro import std.macro.assert; 4 | 5 | void main() { 6 | assert(callFp(&returnFive) == 5); 7 | } 8 | 9 | int callFp(int function() fn) { return fn(); } 10 | 11 | int returnFive() { return 5; } 12 | -------------------------------------------------------------------------------- /test/runnable/hashmap.nt: -------------------------------------------------------------------------------- 1 | module hashmap; 2 | 3 | macro import std.macro.assert; 4 | 5 | import std.algorithm : sort; 6 | 7 | void main() { 8 | mut int[string] map; 9 | 10 | map["foo"] = 5; 11 | map["bar"] = 6; 12 | map["foo"] = 7; 13 | 14 | assert(map["foo"] == 7); 15 | assert(map["bar"] == 6); 16 | 17 | assert(map.has("bar")); 18 | assert(!map.has("baz")); 19 | 20 | { 21 | mut int calls; 22 | int deflt() { calls += 1; return 7; } 23 | assert(map.get("bar", deflt) == 6); 24 | assert(calls == 0); 25 | assert(map.get("baz", deflt) == 7); 26 | assert(calls == 1); 27 | } 28 | assert(map.keys == ["foo", "bar"] || map.keys == ["bar", "foo"]); 29 | assert(map.values.sort((a, b) => a < b) == [6, 7]); 30 | map.remove("foo"); 31 | assert(map.values == [6]); 32 | map.remove("bar"); 33 | assert(map.values.empty); 34 | 35 | mut int[int] map2; 36 | map2[0] = 1; 37 | map2[1] = 1; 38 | assert(map2.has(0)); 39 | map2.clear; 40 | assert(map2.keys.empty); 41 | assert(map2.values.empty); 42 | 43 | mut int[S] map3; 44 | map3[S(3)] = 0; 45 | assert(map3.keys == [S(3)] && map3.values == [0]); 46 | 47 | mut int[int[]] map4; 48 | map4[[2]] = 0; 49 | assert(map4.keys == [[2]] && map4.values == [0]); 50 | 51 | mut int[(:a | :b)] map5; 52 | map5[:a] = 0; 53 | assert(map5.keys == [:a] && map5.values == [0]); 54 | 55 | mut int[(int a, int b)] map6; 56 | map6[(1, 2)] = 0; 57 | assert(map6.keys == [(1, 2)] && map6.values == [0]); 58 | 59 | mut int[vec2f] map7; 60 | map7[vec2f(1, 2)] = 0; 61 | assert(map7.keys == [vec2f(1, 2)] && map7.values == [0]); 62 | 63 | mut int[Enum] map8; 64 | map8[Enum.A] = 0; 65 | assert(map8.keys == [Enum.A] && map8.values == [0]); 66 | } 67 | 68 | enum Enum { A } 69 | 70 | alias vec2f = Vector(float, 2); 71 | 72 | struct S { 73 | int a; 74 | } 75 | -------------------------------------------------------------------------------- /test/runnable/heap_fun.nt: -------------------------------------------------------------------------------- 1 | module heap_fun; 2 | 3 | import neat.runtime : assert, print; 4 | 5 | void main() { 6 | { 7 | int i = 5; 8 | auto lambda = () => i; 9 | assert(lambda() == i); 10 | } 11 | { 12 | int i = 5; 13 | auto lambda = new () => i; 14 | assert(lambda() == i); 15 | } 16 | 17 | test_delegates; 18 | test_lambdas; 19 | } 20 | 21 | void test_delegates() { 22 | mut int delegate()[] dgs; 23 | int delegate() test(int i) { 24 | int nest() => i; 25 | return new &nest; 26 | } 27 | for (i in 0 .. 10) dgs ~= test(i); 28 | for (i in 0 .. 10) assert(dgs[i]() == i); 29 | } 30 | 31 | void test_lambdas() { 32 | mut Producer!int[] lambdas; 33 | Producer!int test(int i) { 34 | return lambdaAction(new () => i); 35 | } 36 | for (i in 0 .. 10) lambdas ~= test(i); 37 | for (i in 0 .. 10) assert(lambdas[i].call() == i); 38 | } 39 | 40 | interface Producer(T) { 41 | T call(); 42 | } 43 | 44 | LambdaAction!T lambdaAction(T)(T lambda) { 45 | return new LambdaAction!T(lambda); 46 | } 47 | 48 | class LambdaAction(T) : Producer!int { 49 | T lambda; 50 | this(this.lambda) {} 51 | override int call() { return lambda(); } 52 | } 53 | -------------------------------------------------------------------------------- /test/runnable/here.nt: -------------------------------------------------------------------------------- 1 | module here; 2 | 3 | macro import std.macro.assert; 4 | 5 | import package(compiler).neat.base; 6 | 7 | void main() { 8 | auto loc = __RANGE__; 9 | 10 | // fileId can only be resolved with -file-id-output info, so we can't test for it. 11 | assert(loc.from.row == 7 && loc.from.column == 15 12 | && loc.to.row == 7 && loc.to.column == 24); 13 | test; 14 | } 15 | 16 | void test(LocRange range = __CALLER__) { 17 | assert(range.(from.(row == 12 && column == 4) 18 | && to.(row == 12 && column == 8))); 19 | } 20 | -------------------------------------------------------------------------------- /test/runnable/hex.nt: -------------------------------------------------------------------------------- 1 | module hex; 2 | 3 | macro import std.macro.assert; 4 | 5 | void main() { 6 | assert(0x1234 == 4660); 7 | assert(0x10000000 == 268435456); 8 | assert(-1 == 0xffffFFFF); 9 | assert(0xabcdef == 0xABCDEF); 10 | assert(0xabcdef == 11259375); 11 | assert(0x8000_0000 == -2147483648); 12 | assert(-0xf == -15); 13 | } 14 | -------------------------------------------------------------------------------- /test/runnable/iftest.nt: -------------------------------------------------------------------------------- 1 | module iftest; 2 | 3 | macro import std.macro.assert; 4 | 5 | void main() { 6 | mut int i = 0; 7 | if (1 == 1) i = 1; 8 | assert(i == 1); 9 | if (1 == 1) i = 1; else i = 2; 10 | assert(i == 1); 11 | if (1 == 2) i = 1; else i = 2; 12 | assert(i == 2); 13 | if (!(1 == 1)) assert(false); 14 | if (1 != 1) assert(false); 15 | int k = 3; 16 | if (int k = 5) assert(k == 5); 17 | assert(k == 3); 18 | if (int zero = 0) assert(false); 19 | if let(int zero = 0) { 20 | } else assert(false); 21 | } 22 | -------------------------------------------------------------------------------- /test/runnable/incdec.nt: -------------------------------------------------------------------------------- 1 | module incdec; 2 | 3 | macro import std.macro.assert; 4 | 5 | struct S { int a; } 6 | 7 | void main() { 8 | mut S s; 9 | s.a++; 10 | s.a--; 11 | ++s.a; 12 | --s.a; 13 | assert(s.a == 0); 14 | } 15 | -------------------------------------------------------------------------------- /test/runnable/intfs.nt: -------------------------------------------------------------------------------- 1 | module intfs; 2 | 3 | macro import std.macro.assert; 4 | 5 | import helpers; 6 | 7 | interface A { 8 | string foo(); 9 | } 10 | 11 | interface B { 12 | string bar(); 13 | } 14 | 15 | interface C : A, B { 16 | string baz(); 17 | } 18 | 19 | class X : C { 20 | override string foo() { return "X.foo"; } 21 | override string bar() { return "X.bar"; } 22 | override string baz() { return "X.baz"; } 23 | } 24 | 25 | interface D { 26 | string qux(); 27 | } 28 | 29 | class Y : X, D { 30 | this() { } 31 | 32 | override string foo() { return "Y.foo"; } 33 | override string bar() { return "Y.bar"; } 34 | override string baz() { return "Y.baz"; } 35 | override string qux() { return "Y.qux"; } 36 | } 37 | 38 | void main() { 39 | Y y = new Y; 40 | 41 | void dump() { 42 | print("contents of $(cast(size_t) y):"); 43 | for (i in 0 .. 5) { 44 | print(" $i: $((cast(size_t*) y)[i])"); 45 | } 46 | } 47 | 48 | // dump; 49 | size_t refs() { return (cast(size_t*) y)[1]; } 50 | assert(refs == 1); 51 | 52 | assert(y.foo == "Y.foo"); 53 | assert(y.bar == "Y.bar"); 54 | assert(y.baz == "Y.baz"); 55 | assert(y.qux == "Y.qux"); 56 | assert(y.instanceOf(Y).foo == "Y.foo"); 57 | assert(y.instanceOf(X).foo == "Y.foo"); 58 | assert(y.instanceOf(A).foo == "Y.foo"); 59 | assert(y.instanceOf(B).bar == "Y.bar"); 60 | assert(y.instanceOf(C).baz == "Y.baz"); 61 | 62 | { 63 | X x = y; 64 | assert(x.foo == "Y.foo"); 65 | assert(x.bar == "Y.bar"); 66 | assert(x.baz == "Y.baz"); 67 | assert(x.instanceOf(Y).foo == "Y.foo"); 68 | assert(x.instanceOf(X).foo == "Y.foo"); 69 | assert(x.instanceOf(A).foo == "Y.foo"); 70 | assert(x.instanceOf(B).bar == "Y.bar"); 71 | assert(x.instanceOf(C).baz == "Y.baz"); 72 | 73 | assert(refs == 1); 74 | C c = x; 75 | assert(refs == 1); 76 | assert(c.foo == "Y.foo"); 77 | assert(c.bar == "Y.bar"); 78 | assert(c.baz == "Y.baz"); 79 | assert(c.instanceOf(Y).foo == "Y.foo"); 80 | assert(c.instanceOf(X).foo == "Y.foo"); 81 | assert(c.instanceOf(A).foo == "Y.foo"); 82 | assert(c.instanceOf(B).bar == "Y.bar"); 83 | assert(c.instanceOf(C).baz == "Y.baz"); 84 | assert(refs == 1); 85 | 86 | B b = c; 87 | assert(b.bar == "Y.bar"); 88 | assert(b.instanceOf(Y).foo == "Y.foo"); 89 | assert(b.instanceOf(X).foo == "Y.foo"); 90 | assert(b.instanceOf(A).foo == "Y.foo"); 91 | assert(b.instanceOf(B).bar == "Y.bar"); 92 | assert(b.instanceOf(C).baz == "Y.baz"); 93 | 94 | A a = c; 95 | assert(a.foo == "Y.foo"); 96 | assert(a.instanceOf(Y).foo == "Y.foo"); 97 | assert(a.instanceOf(X).foo == "Y.foo"); 98 | assert(a.instanceOf(A).foo == "Y.foo"); 99 | assert(a.instanceOf(B).bar == "Y.bar"); 100 | assert(a.instanceOf(C).baz == "Y.baz"); 101 | } 102 | assert(refs == 1); 103 | 104 | { 105 | B b2 = y; 106 | assert(b2.bar == "Y.bar"); 107 | 108 | // FIXME why does b3 grab a ref? 109 | B b3 = b2; 110 | assert(b3.bar == "Y.bar"); 111 | 112 | { 113 | mut B b1 = y; 114 | assert(b1.instanceOf(Y)); 115 | mut nullable B b2 = y.instanceOf(B); 116 | assert(b2.instanceOf(Y)); 117 | } 118 | } 119 | assert(refs == 1); 120 | } 121 | -------------------------------------------------------------------------------- /test/runnable/lambda_packing.nt: -------------------------------------------------------------------------------- 1 | module lambda_packing; 2 | 3 | void main() { 4 | test_struct; 5 | test_tuple; 6 | test_sumtype; 7 | } 8 | 9 | void test_struct() { 10 | auto lambda = () => 5; 11 | alias L = typeof(lambda); 12 | struct S { 13 | L l; 14 | } 15 | S s = S(lambda); 16 | s.l(); 17 | } 18 | 19 | void test_tuple() { 20 | auto lambda = () => 5; 21 | mut auto tuple = (lambda, 1); 22 | tuple = (lambda, 1); 23 | tuple[0](); 24 | } 25 | 26 | void test_sumtype() { 27 | auto lambda = () => 5; 28 | mut (typeof(lambda) | int) st = 5; 29 | st = lambda; 30 | st.case { 31 | typeof(lambda) l: l(); 32 | int i: {} 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/runnable/lambdas.nt: -------------------------------------------------------------------------------- 1 | module lambdas; 2 | 3 | macro import std.macro.assert; 4 | 5 | void main() { 6 | int i = 2; 7 | auto f = a => a + i; 8 | assert(f(5) == 7); 9 | assert((a => a * i)(5) == 10); 10 | assert(f(2.5) == 4.5); 11 | void foo(typeof(f) g) { 12 | assert(f(3.5) == 5.5); 13 | assert(g(4.5) == 6.5); 14 | } 15 | foo(f); 16 | auto add = (a, b) => a + b; 17 | assert(add(2, 3) == 5); 18 | bugRepro; 19 | assert(globalLambda(2, 3) == 5); 20 | } 21 | 22 | alias globalLambda = (a, b) => a + b; 23 | 24 | void bugRepro() { 25 | int i = 23; 26 | auto foo = j => i == j; 27 | // instantiate at stack depth 0 28 | foo(23); 29 | // instantiate at stack depth 1 30 | bool bar(int j) { return foo(j); } 31 | assert(bar(23)); 32 | } 33 | -------------------------------------------------------------------------------- /test/runnable/llvm.nt: -------------------------------------------------------------------------------- 1 | module llvm; 2 | 3 | void main() { 4 | void nest((vec2i, int, (:a | :b)) arg) { } 5 | nest((vec2i(0), 0, :a)); 6 | } 7 | 8 | alias vec2i = Vector(int, 2); 9 | -------------------------------------------------------------------------------- /test/runnable/macrotest.nt: -------------------------------------------------------------------------------- 1 | module macrotest; 2 | 3 | macro import std.macro.assert; 4 | macro import std.macro.the; 5 | macro import std.macro.listcomprehension; 6 | macro import std.macro.once; 7 | 8 | extern(C) void print(char[]); 9 | 10 | int square(int) { return (the int) * the int; } 11 | 12 | class A { 13 | this() { } 14 | } 15 | 16 | struct S { 17 | int i; 18 | void inc() { i++; } 19 | } 20 | 21 | void listcotest() { 22 | int[] arr = [2, 3, 4]; 23 | assert([a * 2 for a in arr] == [4, 6, 8]); 24 | mut int i; 25 | void foo() { i += 1; } 26 | [foo() for x in arr]; 27 | assert(i == 3); 28 | 29 | assert([any a > 2 for a in arr]); 30 | assert(![any a > 4 for a in arr]); 31 | 32 | // equivalent 33 | assert([first true for a in arr where a > 2 else false]); 34 | assert(![first true for a in arr where a > 4 else false]); 35 | assert([first i for i, a in arr where a > 3 else -1] == 2); 36 | 37 | auto list = [[1, 2], [3, 4], [5, 6]]; 38 | assert([first a[1] for a in list where a[0] > 1 else -1] == 4); 39 | 40 | assert([all a > 1 for a in arr]); 41 | assert(![all a > 2 for a in arr]); 42 | 43 | assert([count a in arr where a > 2] == 2); 44 | assert([sum a for a in arr where a > 2] == 7); 45 | 46 | assert([min x for x in [2, 3, 4, 5] where x > 3] == 4); 47 | assert([min x for x in [2, 3] where x > 3 base 0] == 0); 48 | 49 | assert([max x for x in [2, 3, 4, 5] where x < 4] == 3); 50 | assert([max x for x in [4, 5] where x < 4 base 0] == 0); 51 | 52 | import std.math : abs; 53 | assert([argmax(abs(x)) x for x in [2, -3]] == -3); 54 | 55 | S[] arr = [S(0)]; 56 | [s.inc for s in arr]; 57 | // TODO 58 | // assert(arr[0].i == 1); 59 | } 60 | 61 | class Object { 62 | int i; 63 | 64 | this(this.i) { } 65 | } 66 | 67 | void oncetest() { 68 | Object make(int i) { return once new Object(i); } 69 | Object o1 = make(1), o2 = make(2); 70 | assert(o1.i == 1 && o1 is o2); 71 | } 72 | 73 | void main(string[] args) { 74 | print("macrotest"); 75 | A a = new A; 76 | assert(a); // this would not have worked before, because objects are not bools 77 | listcotest(); 78 | oncetest(); 79 | print("- success"); 80 | assert(square(2) == 4); 81 | } 82 | -------------------------------------------------------------------------------- /test/runnable/malloctest.nt: -------------------------------------------------------------------------------- 1 | module malloctest; 2 | 3 | macro import std.macro.assert; 4 | 5 | extern(C) void* malloc(size_t); 6 | 7 | void main() { 8 | int* ip = cast(int*) malloc(4); 9 | *ip = 3; 10 | assert(*ip == 3); 11 | } 12 | -------------------------------------------------------------------------------- /test/runnable/mathtest.nt: -------------------------------------------------------------------------------- 1 | module mathtest; 2 | 3 | macro import std.macro.assert; 4 | 5 | import std.algorithm; 6 | import std.math; 7 | 8 | void main() { 9 | mut int i = 1; 10 | assert(i++ == 1); 11 | assert(++i == 3); 12 | assert(i-- == 3); 13 | assert(--i == 1); 14 | assert(-i == -1); 15 | assert(2 + 3 * 4 == 14); 16 | assert(15 % 4 == 3); 17 | assert((-10 .. 10).map(x => x % 5).array == 18 | [0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4]); 19 | assert(2 << 2 == 8); 20 | assert(5 & 3 == 1); 21 | assert(5 | 3 == 7); 22 | long l = 1_000_000_000_000; 23 | assert(l == cast(long) 1_000_000 * 1_000_000); 24 | assert(abs(sin(π)) <= 0.0001); 25 | assert(1000.0 == 1_000.0); 26 | int a = 2, b = 3; 27 | int id(int i) => i; 28 | assert((a * b).id == 6); 29 | i = 24; 30 | i %= 10; 31 | assert(i == 4); 32 | assert(int.max == 0x7fff_ffff); 33 | assert(int.min == 0x8000_0000); 34 | assert(long.max == 0x7fff_ffff_ffff_ffff); 35 | assert(long.min == 0x8000_0000_0000_0000); 36 | } 37 | -------------------------------------------------------------------------------- /test/runnable/module_level.nt: -------------------------------------------------------------------------------- 1 | module module_level; 2 | 3 | macro import std.macro.assert; 4 | 5 | int foo() { return 1; } 6 | 7 | void main() { 8 | int foo() { return 2; } 9 | assert(foo() == 2); 10 | assert(.foo() == 1); 11 | } 12 | -------------------------------------------------------------------------------- /test/runnable/named_arg.nt: -------------------------------------------------------------------------------- 1 | module named_arg; 2 | 3 | void foo(int i) { } 4 | 5 | class C { 6 | this() { } 7 | void foo(int i) { } 8 | } 9 | 10 | struct S { 11 | int i; 12 | void foo(int i) { } 13 | } 14 | 15 | void main() { 16 | foo(i=1); 17 | (new C).foo(i=1); 18 | S(i=1).foo(i=1); 19 | } 20 | -------------------------------------------------------------------------------- /test/runnable/nested.nt: -------------------------------------------------------------------------------- 1 | module nested; 2 | 3 | macro import std.macro.assert; 4 | 5 | void main() { 6 | { 7 | int i; 8 | } 9 | mut int i; 10 | void foo() { i = 2; } 11 | void bar() { foo(); } 12 | bar(); 13 | assert(i == 2); 14 | } 15 | -------------------------------------------------------------------------------- /test/runnable/nested_import.nt: -------------------------------------------------------------------------------- 1 | module nested_import; 2 | 3 | extern(C) void assert(bool); 4 | 5 | int print(string s) { assert(s == ""); return 5; } 6 | 7 | void foo() { 8 | import neat.runtime : print; 9 | print("Hello World"); 10 | } 11 | 12 | void main() { 13 | foo; 14 | assert(print("") == 5); 15 | } 16 | -------------------------------------------------------------------------------- /test/runnable/nestedfntest.nt: -------------------------------------------------------------------------------- 1 | module nestedfntest; 2 | 3 | macro import std.macro.assert; 4 | 5 | void callme(void delegate!() dg) { 6 | dg(); 7 | } 8 | 9 | class Class { 10 | int value; 11 | this(this.value) { } 12 | } 13 | 14 | // this was a bug that cost me a day. 15 | void bugtest() { 16 | nullable Class a = new Class(5), b = new Class(6); 17 | if (auto a = a) { 18 | void test() { 19 | assert(a.value == 5); 20 | } 21 | test; 22 | } 23 | } 24 | 25 | void templatedNestedFnTest() { 26 | template foo(T) { 27 | T foo(T t) { 28 | return t; 29 | } 30 | } 31 | assert(foo!int(3) == 3); 32 | assert(foo!string("hello") == "hello"); 33 | } 34 | 35 | void templatedNestedFnTest2() { 36 | int i = 3; 37 | template foo(T) { 38 | T foo(T t) { 39 | return t + i; 40 | } 41 | } 42 | void subfun() { 43 | assert(foo!int(4) == 7); 44 | } 45 | subfun; 46 | } 47 | 48 | void sameName() { 49 | mut int a; 50 | { 51 | void foo() { a = 5; } 52 | foo; assert(a == 5); 53 | } 54 | { 55 | void foo() { a = 6; } 56 | foo; assert(a == 6); 57 | } 58 | } 59 | 60 | void funLiteral() { 61 | int call(int i, int delegate!(int) dg) { 62 | return dg(i); 63 | } 64 | int fn(int i) { return i; } 65 | assert(call(4, &fn) == 4); 66 | assert(call(5, (int i) { return i; }) == 5); 67 | } 68 | 69 | int mutualRecursionTest() { 70 | // nested functions without a variable definition between can mutually recurse 71 | int collatz(int x) { 72 | if (x == 1) return 1; 73 | if (x % 2 == 0) return even(x); 74 | return odd(x); 75 | } 76 | int even(int x) => collatz(x / 2); 77 | int odd(int x) => collatz(x * 3 + 1); 78 | assert(collatz(27) == 1); 79 | } 80 | 81 | void main() { 82 | mut int i = 1; 83 | void incr() { i += 1; } 84 | incr(); 85 | assert(i == 2); 86 | callme(&incr); 87 | assert(i == 3); 88 | bugtest; 89 | templatedNestedFnTest; 90 | templatedNestedFnTest2; 91 | sameName; 92 | funLiteral; 93 | nestFnInstantiationTest; 94 | mutualRecursionTest; 95 | } 96 | 97 | void nestFnInstantiationTest() { 98 | int i = 23; 99 | template foo(T) { 100 | bool foo(T j) { 101 | return i == j; 102 | } 103 | } 104 | // instantiate at stack depth 0 105 | foo(23); 106 | // instantiate at stack depth 1 107 | bool bar(int j) { return foo(j); } 108 | assert(bar(23)); 109 | } 110 | -------------------------------------------------------------------------------- /test/runnable/numbers.nt: -------------------------------------------------------------------------------- 1 | module numbers; 2 | 3 | macro import std.macro.assert; 4 | 5 | void main() { 6 | assert(1_234 == 1234); 7 | assert(0xff_ff == 0xffff); 8 | assert(0b10_10 == 0b1010); 9 | } 10 | -------------------------------------------------------------------------------- /test/runnable/obj_is.nt: -------------------------------------------------------------------------------- 1 | module obj_is; 2 | 3 | macro import std.macro.assert; 4 | 5 | class Object 6 | { 7 | this() { } 8 | } 9 | 10 | void main() { 11 | auto a = new Object; 12 | auto b = new Object; 13 | assert(a is a); 14 | assert(!(a is b)); 15 | assert(a !is b); 16 | assert(!(a !is a)); 17 | } 18 | -------------------------------------------------------------------------------- /test/runnable/opoverload.nt: -------------------------------------------------------------------------------- 1 | module opoverload; 2 | 3 | macro import std.macro.assert; 4 | 5 | struct S { 6 | int a; 7 | bool opEquals(int other) => a == other; 8 | } 9 | 10 | void main() { 11 | S s = S(5); 12 | assert(s == S(5)); 13 | assert(s != S(6)); 14 | assert(s == 5); 15 | assert(s != 6); 16 | } 17 | -------------------------------------------------------------------------------- /test/runnable/order.nt: -------------------------------------------------------------------------------- 1 | module order; 2 | 3 | void main() { foo(); auto a = new A; S s; } 4 | 5 | void foo() { bar(); } 6 | 7 | void bar() { } 8 | 9 | class A : B { this() { } } 10 | 11 | class B { } 12 | 13 | struct S { T t; } 14 | 15 | struct T { } 16 | -------------------------------------------------------------------------------- /test/runnable/overloading.nt: -------------------------------------------------------------------------------- 1 | module overloading; 2 | 3 | macro import std.macro.assert; 4 | 5 | int test(int i = 2, int k = 3) 6 | { 7 | assert(i == 2); 8 | assert(k == 3); 9 | return 1; 10 | } 11 | 12 | int test(bool b) 13 | { 14 | return 2; 15 | } 16 | 17 | class Foo 18 | { 19 | (int | string) value; 20 | this() { } 21 | this(int i) { this.value = i; } 22 | this(string s) { this.value = s; } 23 | int foo(int i) { return 1; } 24 | int foo(string s) { return 2; } 25 | } 26 | 27 | class Bar : Foo 28 | { 29 | override int foo(int i) { return 3; } 30 | override int foo(string s) { return 4; } 31 | } 32 | 33 | struct S { 34 | this(int) {} 35 | this(string) {} 36 | } 37 | 38 | void main() 39 | { 40 | assert(test(2) == 1); 41 | assert(test(true) == 2); 42 | assert(test == 1); 43 | assert((new Foo(3)).value == 3); 44 | assert((new Foo("foo")).value == "foo"); 45 | Foo barFoo() { return new Bar; } 46 | assert(barFoo.foo(1) == 3); 47 | assert(barFoo.foo("foo") == 4); 48 | auto s1 = S(5); 49 | auto s2 = S("foo"); 50 | } 51 | -------------------------------------------------------------------------------- /test/runnable/parentest.nt: -------------------------------------------------------------------------------- 1 | module parentest; 2 | 3 | macro import std.macro.assert; 4 | 5 | struct S 6 | { 7 | int a; 8 | } 9 | 10 | void main() { 11 | int b = 4; 12 | auto s = S(2); 13 | void test() { 14 | assert(s.(a + b) == 6); 15 | } 16 | test; 17 | } 18 | -------------------------------------------------------------------------------- /test/runnable/private_.nt: -------------------------------------------------------------------------------- 1 | module private_; 2 | 3 | macro import std.macro.assert; 4 | 5 | class A { 6 | private int i; 7 | this(this.i) { } 8 | bool same(A other) { 9 | return i == other.i; 10 | } 11 | } 12 | 13 | struct S { 14 | private int i; 15 | bool same(S other) { 16 | return this.i == other.i; 17 | } 18 | } 19 | 20 | void main() { 21 | assert((new A(5)).same(new A(5))); 22 | assert(!S(6).same(S(8))); 23 | } 24 | -------------------------------------------------------------------------------- /test/runnable/ptrarraytest.nt: -------------------------------------------------------------------------------- 1 | module ptrarraytest; 2 | 3 | macro import std.macro.assert; 4 | 5 | extern(C) void* malloc(size_t); 6 | extern(C) void free(void*); 7 | 8 | void main() { 9 | int* ip = cast(int*) malloc(40); 10 | mut int i = 0; 11 | while (i < 10) { 12 | ip[i] = i; 13 | i += 1; 14 | } 15 | i = 0; 16 | mut int sum = 0; 17 | while (i < 10) { 18 | sum = sum + ip[i]; 19 | i += 1; 20 | } 21 | assert(sum == 45); 22 | free(ip); 23 | } 24 | -------------------------------------------------------------------------------- /test/runnable/ptrtest.nt: -------------------------------------------------------------------------------- 1 | module ptrtest; 2 | 3 | macro import std.macro.assert; 4 | 5 | void main() { 6 | mut int i = 0; 7 | setPtr(&i, 5); 8 | assert(i == 5); 9 | } 10 | 11 | void setPtr(int* ip, int value) { 12 | *ip = value; 13 | } 14 | -------------------------------------------------------------------------------- /test/runnable/public_import_test.nt: -------------------------------------------------------------------------------- 1 | module public_import_test; 2 | 3 | import public_import; 4 | 5 | void main() { 6 | foo; 7 | } 8 | -------------------------------------------------------------------------------- /test/runnable/quote_struct.nt: -------------------------------------------------------------------------------- 1 | module quote_struct; 2 | 3 | macro import std.macro.quasiquoting; 4 | 5 | import package(compiler).neat.base; 6 | import package(compiler).neat.runtime; 7 | import package(compiler).neat.struct_; 8 | 9 | class ASTQuoteTest : ASTStatement 10 | { 11 | ASTStatement child; 12 | 13 | this(this.child) { } 14 | 15 | override (StatementCompileResult | Error) compile(Context context) { 16 | auto childExpr = context.compiler.astStatementExpression( 17 | [child], context.compiler.astNumberLiteral(0)); 18 | auto stmt = context.compiler.$stmt { 19 | struct Test 20 | { 21 | int i; 22 | int foo() { return i * i; } 23 | } 24 | $childExpr; 25 | }; 26 | return stmt.compile(context); 27 | } 28 | } 29 | 30 | class QuoteTest : Macro 31 | { 32 | this() { } 33 | override void apply(MacroArgs args) { 34 | auto args = args.instanceOf(ParseStatementArgs); 35 | if (args) { 36 | args.statement = this.parse(args.parser, args.lexicalContext); 37 | } 38 | } 39 | 40 | (nullable ASTStatement | Error) parse(Parser parser, LexicalContext lexicalContext) { 41 | auto compiler = lexicalContext.compiler; 42 | auto loc = parser.loc; 43 | 44 | parser.begin; 45 | if (!parser.accept("quotetest")) { 46 | parser.revert; 47 | return null; 48 | } 49 | parser.commit; 50 | auto stmt = compiler.parseStatement(parser, lexicalContext)?; 51 | return new ASTQuoteTest(stmt); 52 | } 53 | } 54 | 55 | void addTestMacro(MacroState macroState) { 56 | macroState.addMacro(new QuoteTest); 57 | } 58 | 59 | macro(addTestMacro); 60 | 61 | void main() { 62 | quotetest { 63 | Test t = Test(5); 64 | assert(t.foo == 25); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /test/runnable/scientific.nt: -------------------------------------------------------------------------------- 1 | module scientific; 2 | 3 | macro import std.macro.assert; 4 | 5 | void main() { 6 | assert(13e0 == 13); 7 | assert(1e2 == 100); 8 | assert(1.0e2 == 100.0); 9 | assert(1.5e2 == 150); 10 | assert(1e-2 == 0.01); 11 | assert(-1e-1-1 == -1.1); 12 | } 13 | -------------------------------------------------------------------------------- /test/runnable/sha256.nt: -------------------------------------------------------------------------------- 1 | module sha256; 2 | 3 | macro import std.macro.assert; 4 | macro import std.macro.listcomprehension; 5 | 6 | import std.sha256; 7 | import std.string; 8 | 9 | void main() { 10 | auto digest = new Sha256; 11 | // TODO cast(ubyte[]) 12 | digest.update([cast(ubyte) a for a in "Hello World"]); 13 | auto result = digest.finalize.toHexString; 14 | assert(result == "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e"); 15 | } 16 | -------------------------------------------------------------------------------- /test/runnable/shortcircuittest.nt: -------------------------------------------------------------------------------- 1 | module shortcircuittest; 2 | 3 | macro import std.macro.assert; 4 | 5 | void main() { 6 | { 7 | mut int a, b; 8 | set(&a, 1) && set(&b, 1); 9 | assert(a == 1); assert(b == 1); 10 | } 11 | { 12 | mut int a, b; 13 | set(&a, 0) && set(&b, 1); 14 | assert(a == 1); assert(b == 0); 15 | } 16 | { 17 | mut int a, b; 18 | set(&a, 0) && set(&b, 0); 19 | assert(a == 1); assert(b == 0); 20 | } 21 | { 22 | mut int a, b; 23 | set(&a, 1) || set(&b, 1); 24 | assert(a == 1); assert(b == 0); 25 | } 26 | { 27 | mut int a, b; 28 | set(&a, 0) || set(&b, 1); 29 | assert(a == 1); assert(b == 1); 30 | } 31 | { 32 | mut int a, b; 33 | set(&a, 0) || set(&b, 0); 34 | assert(a == 1); assert(b == 1); 35 | } 36 | } 37 | 38 | int set(int* ip, int i) 39 | { 40 | *ip = 1; 41 | return i; 42 | } 43 | -------------------------------------------------------------------------------- /test/runnable/stringtest.nt: -------------------------------------------------------------------------------- 1 | module stringtest; 2 | 3 | macro import std.macro.assert; 4 | 5 | extern(C) void print(char[]); 6 | 7 | void main() { 8 | string str = "Hello World"; 9 | print(str); 10 | assert("\n" == " 11 | "); 12 | assert(`"\n"` == "\"\\n\""); 13 | // somewhat like rust 14 | assert("\ 15 | foo \ 16 | 17 | bar" == "foo \n bar"); 18 | } 19 | -------------------------------------------------------------------------------- /test/runnable/structtest.nt: -------------------------------------------------------------------------------- 1 | module structtest; 2 | 3 | macro import std.macro.assert; 4 | 5 | extern(C) void* malloc(size_t); 6 | extern(C) void free(void*); 7 | 8 | void main() { 9 | mut S s; 10 | s.a = 3; 11 | int* ap = &s.a; 12 | assert(*ap == 3); 13 | 14 | void* mem = malloc(8); 15 | S* sp = cast(S*) mem; 16 | int* ip = cast(int*) mem; 17 | sp.a = 5; 18 | sp.b = 8; 19 | assert(sp.a == 5); 20 | assert(*ip == 5); 21 | (*sp).a = 6; 22 | assert(sp[0].a == 6); 23 | 24 | S t = S(2, 3); 25 | assert(t.a == 2); 26 | assert(t.b == 3); 27 | assert(t.sum() == 5); 28 | free(mem); 29 | 30 | mut int count; 31 | S test() { count += 1; return S(2, 3); } 32 | assert(test.sum == 5); 33 | assert(count == 1); 34 | 35 | assert(S.init.sum == 5); 36 | 37 | assert((new A).test.i == 5); 38 | 39 | assert(S(3).sum == 8); 40 | 41 | assert(T(2, 3).((a, b)) == (3, 2)); 42 | assert(T(3).((a, b)) == (5, 3)); 43 | } 44 | 45 | struct S 46 | { 47 | int a; 48 | int b = 5; 49 | int sum() { return a + b; } 50 | static S init() { return S(2, 3); } 51 | } 52 | 53 | struct T 54 | { 55 | int a; 56 | int b; 57 | this(this.b, this.a = 5) {} 58 | } 59 | 60 | class A 61 | { 62 | this() { } 63 | struct X { int i; } 64 | X test() { 65 | struct Y { 66 | int i; 67 | int get() { return i; } 68 | } 69 | Y y = Y(5); 70 | return X(y.get); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /test/runnable/symbol_identifier.nt: -------------------------------------------------------------------------------- 1 | module symbol_identifier; 2 | 3 | macro import std.macro.assert; 4 | 5 | void main() { 6 | (:integer, int | :floating, float) i = (:integer, 2); 7 | bool isInteger = i.case((:integer, int i): true, (:floating, float f): false); 8 | int value = i.case((:integer, int i): i, (:floating, float f): cast(int) f); 9 | assert(value == 2); 10 | } 11 | -------------------------------------------------------------------------------- /test/runnable/templates.nt: -------------------------------------------------------------------------------- 1 | module templates; 2 | 3 | macro import std.macro.assert; 4 | 5 | import helpers; 6 | 7 | class Class(T) : Intf!T { 8 | override void test() { } 9 | this() { } 10 | } 11 | 12 | interface Intf(T) { 13 | void test(); 14 | } 15 | 16 | alias Alias1 = Class!int; 17 | alias Alias2 = Class!int; 18 | 19 | alias id(T) = T; 20 | 21 | template LinkedList(T) { 22 | class LinkedList { 23 | this() { } 24 | mut LinkedList next; 25 | mut T value; 26 | } 27 | } 28 | 29 | void linkedList() { 30 | auto ll = new LinkedList!int(); 31 | ll.next = new LinkedList!int(); 32 | ll.value = 5; 33 | ll.next.value = 6; 34 | } 35 | 36 | template init(T) { 37 | T init() { 38 | mut uninitialized T t; 39 | return t; 40 | } 41 | } 42 | 43 | typeof(init!T + init!U) max(T, U)(T a, U b) { 44 | if (a > b) return a; 45 | return b; 46 | } 47 | 48 | void pair() { 49 | struct Pair(T) { 50 | T first, second; 51 | } 52 | auto pair = Pair!int(1, 2); 53 | } 54 | 55 | void maxTypes() { 56 | assert(max!(int, int)(2, 3) == 3); 57 | assert(max!(float, int)(3.5f, 2) == 3.5f); 58 | } 59 | 60 | T notNull(T)(nullable T arg) { 61 | if (auto a = arg) return a; 62 | assert(false); 63 | } 64 | 65 | class Object { this() { } } 66 | 67 | void notNullTest() { 68 | nullable Object foo = new Object; 69 | Object bar = foo.notNull; 70 | } 71 | 72 | class ClassTest { 73 | this() { } 74 | void foo(T)(T arg) { } 75 | } 76 | 77 | R inferReturnTypeTest(R)(R delegate!() dg) => dg(); 78 | 79 | void main() { 80 | (new Alias1).test; 81 | notNullTest; 82 | id!int i = 5; 83 | linkedList; 84 | maxTypes; 85 | pair; 86 | (new ClassTest).foo(5); 87 | int nested() => 5; 88 | assert(inferReturnTypeTest(&nested) == 5); 89 | } 90 | -------------------------------------------------------------------------------- /test/runnable/ternary.nt: -------------------------------------------------------------------------------- 1 | module ternary; 2 | 3 | macro import std.macro.assert; 4 | 5 | void main() { 6 | (int, int, int, int) test(int i) { 7 | mut int aCalled = 0, bCalled = 0, cCalled = 0; 8 | int a() { 9 | aCalled ++; 10 | return 2; 11 | } 12 | int b() { 13 | bCalled ++; 14 | return 3; 15 | } 16 | int c() { 17 | cCalled ++; 18 | return 4; 19 | } 20 | int ret = a if i == 0 else b if i == 1 else c; 21 | return (aCalled, bCalled, cCalled, ret); 22 | } 23 | assert(test(0) == (1, 0, 0, 2)); 24 | assert(test(1) == (0, 1, 0, 3)); 25 | assert(test(2) == (0, 0, 1, 4)); 26 | } 27 | -------------------------------------------------------------------------------- /test/runnable/tuples.nt: -------------------------------------------------------------------------------- 1 | module tuples; 2 | 3 | macro import std.macro.assert; 4 | 5 | void main() { 6 | (int, float) tuple = (2, 3.0f); 7 | assert(tuple[0] == 2 && tuple[1] == 3.0); 8 | (int i, float) fun() { return (2, 3.0f); } 9 | assert(fun.i == 2); 10 | assert(tuple == fun); 11 | } 12 | -------------------------------------------------------------------------------- /test/runnable/typeof.nt: -------------------------------------------------------------------------------- 1 | module typeof; 2 | 3 | macro import std.macro.assert; 4 | 5 | void main() { 6 | int foo() { assert(false); } 7 | 8 | typeof(foo()) bla = 5; 9 | } 10 | -------------------------------------------------------------------------------- /test/runnable/ufcs.nt: -------------------------------------------------------------------------------- 1 | module ufcs; 2 | 3 | macro import std.macro.assert; 4 | 5 | import helpers; 6 | 7 | struct S 8 | { 9 | int i; 10 | int bar() { return i; } 11 | } 12 | 13 | int getI(S s) { return s.i; } 14 | int bar(S s) { return 6; } 15 | 16 | void main() { 17 | S s = S(5); 18 | assert(s.getI == 5); 19 | assert(s.getI() == 5); 20 | assert(s.bar() == 5); 21 | assert(s.bar == 5); 22 | 23 | assert((new Test2).add(2, 3) == 5); 24 | 25 | assert((4).ident == 4); 26 | assert((4).ident!int == 4); 27 | assert(3.addT!int(3) == 6); 28 | 29 | "Success!".print; 30 | } 31 | 32 | template ident(T) { 33 | T ident(T value) { return value; } 34 | } 35 | 36 | // ufcs does not match class methods 37 | 38 | int add(int a, int b) { return a + b; } 39 | 40 | template addT(T) { 41 | T addT(T a, T b) { return a + b; } 42 | } 43 | 44 | class Test2 45 | { 46 | this() { } 47 | int add(int a, int b) { 48 | // this.add(a, b) is not a candidate 49 | return a.add(b); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /test/runnable/vartest.nt: -------------------------------------------------------------------------------- 1 | module vartest; 2 | 3 | macro import std.macro.assert; 4 | 5 | void main() { 6 | int a; 7 | assert(a == 0); 8 | int b = 3; 9 | assert(b == 3); 10 | int c, d; 11 | assert(c == 0 && d == 0); 12 | int e = 4, f; 13 | assert(e == 4 && f == 0); 14 | int g, h = 5; 15 | assert(g == 0 && h == 5); 16 | int i = 6, j = 7; 17 | assert(i == 6 && j == 7); 18 | int k = 5, l = k; 19 | assert(k == 5 && l == 5); 20 | auto m = 0.5, n = 7; 21 | assert(m / 2 == 0.25 && n / 2 == 3); 22 | // is this a good idea??? 23 | auto = 2, = 3; 24 | } 25 | -------------------------------------------------------------------------------- /test/runnable/vectortest.nt: -------------------------------------------------------------------------------- 1 | module vectortest; 2 | 3 | macro import std.macro.assert; 4 | 5 | alias vec3f = Vector(float, 3); 6 | 7 | void main() { 8 | auto v1 = vec3f(1, 2, 4); 9 | assert(v1.x == 1 && v1.y == 2 && v1.z == 4); 10 | assert(v1.zyx == vec3f(4, 2, 1)); 11 | assert(v1 * 2 == vec3f(2, 4, 8)); 12 | assert(v1 / 2 == vec3f(0.5f, 1, 2)); 13 | assert(v1 + v1 == vec3f(2, 4, 8)); 14 | int i = 2; 15 | assert(v1[i] == 4); 16 | mut auto v2 = v1; 17 | v2[1] = 5; 18 | int two = 2; 19 | v2[two] = 6; 20 | assert(v2 == vec3f(1, 5, 6)); 21 | } 22 | -------------------------------------------------------------------------------- /test/runnable/versions.nt: -------------------------------------------------------------------------------- 1 | module versions; 2 | 3 | macro import std.macro.assert; 4 | 5 | struct S { 6 | int a; 7 | version (generation1) int b; 8 | version (doesntExist) string c; 9 | } 10 | 11 | class C { 12 | int a; 13 | version (generation1) int b; 14 | version (doesntExist) string c; 15 | this(this.a, this.b) { } 16 | } 17 | 18 | version (doesNotExist) { 19 | int foo() { return 5; } 20 | } else { 21 | import std.math : sin; 22 | int foo() { return 6; } 23 | } 24 | 25 | int bar() { 26 | version (doesNotExist) { 27 | int a = 5; 28 | } else { 29 | int a = 6; 30 | } 31 | return a; 32 | } 33 | 34 | void main() { 35 | S s = S(2, 3); 36 | C c = new C(2, 3); 37 | assert(foo == 6); 38 | sin(0.5f); 39 | assert(bar == 6); 40 | } 41 | -------------------------------------------------------------------------------- /test/runnable/weird.nt: -------------------------------------------------------------------------------- 1 | module weird; 2 | 3 | extern(C) void assert(int); 4 | 5 | void main() { 6 | int foo() { return return 2; } 7 | assert(foo == 2); 8 | int bar() { 9 | while (true) return break; 10 | return 3; 11 | } 12 | assert(bar == 3); 13 | } 14 | -------------------------------------------------------------------------------- /test/runnable/whiletest.nt: -------------------------------------------------------------------------------- 1 | module whiletest; 2 | 3 | macro import std.macro.assert; 4 | 5 | void main() { 6 | assert(whiletest(5) == 32); 7 | 8 | mut int i; 9 | mut int k; 10 | while (k < 10) { 11 | k += 1; 12 | if (k < 3) continue; 13 | i += 1; 14 | if (k > 5) break; 15 | } 16 | assert(i == 4); 17 | while (true) true && break; 18 | while (true) false || break; 19 | } 20 | 21 | int whiletest(int k) { 22 | mut int k = k; // TODO (mut int k) 23 | mut int i = 1; 24 | while (k > 0) { 25 | i = i * 2; 26 | k = k - 1; 27 | } 28 | return i; 29 | } 30 | -------------------------------------------------------------------------------- /test/runnable/withtest.nt: -------------------------------------------------------------------------------- 1 | module withtest; 2 | 3 | macro import std.macro.assert; 4 | 5 | struct S 6 | { 7 | int i, k; 8 | } 9 | 10 | enum E 11 | { 12 | A, 13 | B 14 | } 15 | 16 | void withType() { 17 | auto a = E.A; 18 | with (E) { 19 | assert(A == a); 20 | } 21 | assert(E.([A, B]) == [E.A, E.B]); 22 | } 23 | 24 | void main() { 25 | with (S(5, 3)) 26 | { 27 | void nested() { 28 | assert(i + k == 8); 29 | } 30 | assert(i + k == 8); 31 | nested; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /unittest.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | PACKAGES="-Pbuild:build:src -Psrc:src:compiler" 4 | FLAGS="-lm -lpthread -j8" 5 | RUN="build/unittest" 6 | 7 | if [ "${1:-}" = "win64" ]; then 8 | FLAGS="${FLAGS} --target x86_64-w64-mingw32" 9 | RUN="wine build/unittest.exe" 10 | function filter() { 11 | grep -v ' neat' |grep -v ' backend' |grep -v 'main' | grep -v 'std.macro' |\ 12 | grep -v 'std.time' | # TODO \ 13 | grep -v 'std.socket' | # TODO \ 14 | grep -v 'std.process' # TODO 15 | } 16 | shift 17 | else 18 | . ./find-llvm-config.sh 19 | FLAGS="${FLAGS} -I$($LLVM_CONFIG --includedir) -L-L$($LLVM_CONFIG --libdir) -version=LLVMBackend" 20 | function filter() { 21 | cat 22 | } 23 | fi 24 | 25 | mkdir -p build 26 | ( 27 | echo "module unittest;" 28 | find src/ -name \*.nt |sed -e 's,/,.,g' -e 's/^src.\(.*\).nt$/import \1;/' |filter 29 | ) > build/unittest.nt 30 | 31 | 32 | neat --unittest=src --no-main $PACKAGES $FLAGS build/unittest.nt -o build/unittest 33 | eval "${RUN}" 34 | --------------------------------------------------------------------------------