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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Tree-sitter C sources are generated from ext/tree-sitter-andy-cpp/grammar.js.
ext/tree-sitter-andy-cpp/src/*.c linguist-generated=true
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
- Run `cargo fmt`
- Run `cargo clippy` and fix all warnings
- Ensure all tests pass (`cargo test`)
- Do not leave `TODO` comments in code — either fix the issue immediately or open a GitHub issue and record it in `TODO.md`
- Do not leave `TODO` comments in code — either fix the issue immediately or open a GitHub issue

## Common Commands

Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ cargo test
```

If you find yourself writing a `TODO` comment, please open a GitHub
issue instead and record it in [`TODO.md`](TODO.md).
issue instead.

## Git conventions

Expand Down
192 changes: 25 additions & 167 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
# Andy C++

Andy C++ is a small scripting language built for personal use — primarily for solving [Advent of Code](https://adventofcode.com/) puzzles and quick one-off scripts. It is not intended for production use.
Andy C++ is a programming language built primarily for solving [Advent of Code](https://adventofcode.com/) puzzles and quick one-off scripts. It's syntax and semantics are designed to feel familiar to those who know Rust, while offering some of the high-level flexibility from Python (without all of its pitfalls).

## Clanker Disclosure
Since version 0.3.0 the language uses a custom byte-code VM based on the Crafting Interpreters book. Code is: lexed, parsed, analysed, compiled and executed in separate steps. The project ships as a single binary that contains everything including a REPL, LSP and basic profiler.

After the 0.2.0 release I've been using this project as a playground to experiment and learn more about AI tools such as
Claude Code. I still spend countless hours pondering every little decision, but in the end most of the new code is
written by AI. I understand a lot of people are uncomfortable with that, and if that means you'd rather stay away from
this project then that's totally fine. If you would like to contribute to this project using AI tools, all I ask you is
that you (the human) carefully review your submissions and that you include in your PR description which parts were
generated by AI and which models you've used.

_This section was written by a human._
**Features:**
* Arbitrary-precision arithmetic (including rational numbers)
* A dynamic type system that tries to rescue you with static type checks ahead of compilation
* Higher order functions and closures
* `a.map(b)` and `map(a,b)` are [exactly the same](https://timfennis.github.io/andy-cpp/features/method-call-syntax.html)
* Functions that take two arguments can be used in [augmented assignment](https://timfennis.github.io/andy-cpp/features/augmented-assignment.html): `l map= fn(x) => x + 3`
* Marking a function `pure` enables [memoization](https://timfennis.github.io/andy-cpp/features/memoization.html) (but only through hashing, no equality checks; use at your own risk)
* Built in support for default dictionaries, MinHeap, MaxHeap and Deque
* A pretty rich but work in progress standard library

## Getting Started

The best way to try this project is to build it from source using the rust toolchain. There are binary releases but those are symbollic milestones, and contain bugs.

### Prerequisites

You need a working [Rust toolchain](https://rustup.rs/).

### Install

```bash
cargo install --path ndc_bin
cargo install --git https://github.com/timfennis/andy-cpp
```

This installs the `ndc` binary. You can then run a script:
Expand All @@ -41,174 +44,29 @@ To browse the built-in function documentation:

```bash
ndc docs
ndc docs sort # filter by keyword
ndc docs map # filter by the keyword 'map'
```

For the language manual, see <https://timfennis.github.io/andy-cpp/>.

### VS Code Extension

A VS Code extension is available on the
[Open VSX Registry](https://open-vsx.org/extension/TimFennis/andy-cpp). It provides:

- Syntax highlighting for `.ndc` files
- Language server (LSP) with diagnostics, inlay type hints, and completions
- A "Run Script" command that executes the current file in the integrated terminal
### Editor support

The extension launches the LSP automatically using the `ndc` binary. If `ndc` is not on the PATH
that VS Code uses (common when installed via a shell like fish), set the `andy-cpp.ndcPath` setting
to the full path of the binary.

### JetBrains IDEs (RustRover, IntelliJ, …)

JetBrains IDEs are supported by importing `ext/andy-cpp` as a TextMate bundle (syntax
highlighting) and connecting the LSP4IJ plugin to `ndc lsp` (diagnostics, hover,
completion, inlay type hints, go to definition). See the
Installation and configuration instructions for VS Code, JetBrains IDEs, Neovim,
Helix, and other LSP-capable editors are available on the
[editor support page](https://timfennis.github.io/andy-cpp/tooling/editor-support.html)
in the manual for setup instructions.

### Other editors

If you prefer a different editor, you can start the language server manually over stdio:

```bash
ndc lsp --stdio
```

Point your editor's LSP client at this command for `.ndc` files.

## Example

Currently, the language has quite a lot of features allowing you to write some pretty neat programs.

### Factorial

You can produce very large numbers quite quickly because we use the [num](https://docs.rs/num/latest/num/) crate under
the hood.

```ndc
fn factorial(n) {
if n == 1 {
return 1
}

n * factorial(n - 1)
}

print(factorial(100));

// You can call all functions as if they are methods on an object
print(100.factorial());
```

### Overloading

Functions are matched by name and arity, so you can define multiple versions of the same function.

```ndc
fn add(n) { n + 1 }
fn add(a, b) { a + b }

// add(5) = 6, add(4) = 5, add(6, 5) = 11
print(add(add(5), add(4))); // prints 11
// 5.add() = 6, 4.add() = 5, 6.add(5) = 11
print(add(5).add(4.add())); // prints 11 as well
```

### Use functions as augmented assignment operators

Many functions can be used to
create [augmented assignment operators](https://blog.vero.site/post/noulith#augmented-assignment).

```ndc
let r = 0;
in the manual.

for i in 0..100 {
// roughly translates to r = max(r, i * 8333446703 % 94608103)
r max= i * 8333446703 % 94608103;
}
## Examples

print(r);
```

### Higher-order functions

Anonymous functions can be passed to built-ins like `map`, `filter`, and `sorted`.

```ndc
let numbers = [5, 3, 8, 1, 9, 2, 7];

let evens = numbers.filter(fn(x) => x % 2 == 0);
let squares = numbers.map(fn(x) => x * x);
let top3 = numbers.sorted().reversed()[0..3];

print(evens); // [8, 2]
print(squares); // [25, 9, 64, 1, 81, 4, 49]
print(top3); // [9, 8, 7]
```

### Tuple vectorization

Arithmetic operators work element-wise on tuples of numbers.

```ndc
let a = (1, 2, 3);
let b = (4, 5, 6);

print(a + b); // (5, 7, 9)
print(a * b); // (4, 10, 18)
print(a * 2); // (2, 4, 6)
print(10 - a); // (9, 8, 7)
```

### Maps and Sets

Maps and sets share the same `%{}` syntax.

```ndc
let map = %{"foo": "bar", "baz": 42};
print(map["foo"]); // bar

let set = %{1, 2, 3, 4};
print(3 in set); // true
```

A default value for missing keys can be specified, similar to Python's `defaultdict`.

```ndc
let counts = %{: 0};
for word in ["apple", "banana", "apple", "cherry", "banana", "apple"] {
counts[word] += 1;
}
print(counts["apple"]); // 3
print(counts["banana"]); // 2
```

### List comprehensions

The language supports list comprehensions with the same semantics as Haskell but a syntax slightly more similar to
Python.

```ndc
fn pythagorean_triples(n) {
return [(a, b, c) for a in 1..=n,
b in a..=n,
c in b..=n,
if a ^ 2 + b ^ 2 == c ^ 2]
}
```

The same features are also available in regular for iterations.

```ndc
for a in 1..=25, b in a..=25, c in b..=25, if a ^ 2 + b ^ 2 == c ^ 2 {
print(a, b, c);
}
```
Many examples of the language can be found in [this](https://github.com/timfennis/advent-of-code-ndc) repository.

## Thanks

This language and implementation was inspired by Robert Nystrom's
book [Crafting Interpreters](https://craftinginterpreters.com/). I've also taken plagiaristic levels of inspiration
from [Noulith](https://github.com/betaveros/noulith) which is the language that inspired me to read the book in the
first place.

## LLM Disclosure

This project has had various levels of LLM involvement during its lifetime. The codebase is designed by humans and is meant to be read and maintained primarily by humans. Large language models are tools and, like all other tools, have strengths and limitations. They are allowed in this project when used responsibly. All contributions will be judged on their merits.
96 changes: 0 additions & 96 deletions TODO.md

This file was deleted.

Loading
Loading