├── .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 |
--------------------------------------------------------------------------------