Skip to content

Commit

Permalink
Merge pull request #3 from Shubhamai/master
Browse files Browse the repository at this point in the history
Master
  • Loading branch information
Shubhamai authored Jun 21, 2024
2 parents 6dd2d2b + 08498b4 commit c00f584
Show file tree
Hide file tree
Showing 40 changed files with 797 additions and 5,417 deletions.
5 changes: 1 addition & 4 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
/target
notes.txt
.gitignore
pkg/
web/node_modules
web/.next
.gitignore
12 changes: 12 additions & 0 deletions example.ai
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@
// https://arogozhnikov.github.io/2023/12/28/fastest-autograd.html
// https://huyenchip.com/2021/09/07/a-friendly-introduction-to-machine-learning-compilers-and-optimizers.html

// from micrograd

// everything is a tensor and autograd tracks it
// built-in support of einops, and common layers (relu, attention, linear...)
// with syntax highlighting, online playground, ide integration, repl

// ast optimizer using gpt/llama perhaps ? ( experiment )
// Q: optimize this ast and only return the answer and nothing else `(. x (f (> 1 2) (!= 3 3)))`
// AI: (. x (f false false))



// fun addPair(a, b) {
// return a + b;
// }
Expand Down
8 changes: 5 additions & 3 deletions examples/intro.ai
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ if (a > 1.0) {
print("a is less than 1");
}

//while (3 > 2) {
// print("gg");
//}
let i = 0;
while (10 > i) {
print(i);
i += 1;
}
26 changes: 8 additions & 18 deletions examples/micrograd.ai
Original file line number Diff line number Diff line change
@@ -1,28 +1,18 @@
// from micrograd

// everything is a tensor and autograd tracks it
// built-in support of einops, and common layers (relu, attention, linear...)
// with syntax highlighting, online playground, ide integration, repl

// ast optimizer using gpt/llama perhaps ? ( experiment )
// Q: optimize this ast and only return the answer and nothing else `(. x (f (> 1 2) (!= 3 3)))`
// AI: (. x (f false false))

let a = -4.0;
let b = 2.0;
let c = a + b;
let d = a * b + b**3;
c += c + 1;
c += 1 + c + (-a);
d += d * 2 + (b + a).relu();
d += 3 * d + (b - a).relu();
let e = c - d;
let f = e**2;
let g = f / 2.0;
g += 10.0 / f;
// d += d * 2 + (b + a).relu();
// d += 3 * d + (b - a).relu();
// let e = c - d;
// let f = e**2;
// let g = f / 2.0;
// g += 10.0 / f;


print(g) // prints 24.7041, the outcome of this forward pass
// g.backward()
// print(g) // prints 24.7041, the outcome of this forward pass
// g.backward() // TODO: implement this
// print(a.grad) # prints 138.8338, i.e. the numerical value of dg/da
// print(b.grad) # prints 645.5773, i.e. the numerical value of dg/db
143 changes: 143 additions & 0 deletions pkg/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# [name]

A simple bytecode compiler written in Rust with stack-based VM.

## Overview





## Getting Started

Try the language in the [playground]([name].vercel.app).

### Example

```bash
cargo install [name]
echo "let a = 10; print(a);" > example.rs
[name] run example.rs
```

Try more examples in the [examples](./examples) directory.

## Features

- [x] Variables
- [x] Arithmetic operations
- [x] Logical operations
- [x] Control flow (if, else)
- [x] Loops (while)
- [ ] Functions
- [ ] Closures
- [ ] Autograd
- [ ] Tensor Support

## Implementation

The source code goes through the following stages:

1. [Tokenization](#tokenization)
2. [Parsing](#parsing)
3. [Compilation](#compilation)
4. [Execution](#execution)

### Tokenization

[scanner.rs](./src/scanner.rs)

Here the scanner/lexer take the raw source code and converts in into a list of [predefined tokens](./src/scanner.rs).

Here I am using rust [logos](https://github.com/maciejhirsz/logos) to create the lexer, which converts the source code into vector of tokens.

For example, the source code:

```rust
let a = 10;
print(a);
```

Get's converted into:

```rust
[LET, Identifier, EQUAL, Number(10.0), SEMICOLON, PRINT, LeftParen, Identifier, RightParen, SEMICOLON]
```

### Parsing

[parser.rs](./src/ast.rs)

The parser takes the list of tokens and converts it into an Abstract Syntax Tree (AST). Here we also handle the precedence of the operators, syntax errors, syntatic sugar like `+=`, `-=`, etc.

For example, the tokens:

```rust
[LET, Identifier, EQUAL, Number(10.0), SEMICOLON, PRINT, LeftParen, Identifier, RightParen, SEMICOLON]
```

Get's converted into:

```rust
Let("a", [Number(10.0)])
Print([Identifier("a")])
```

I am using a combination of recursive descent parser and [pratt parser](https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html) to parse the tokens.

Pratt parser is used to parse the expressions, and recursive descent parser for everything else.

### Compilation

[compiler.rs](./src/compiler.rs)

The compiler takes the AST and converts it into a list of bytecode instructions.

For example, the AST:

```rust
Let("a", [Number(10.0)])
Print([Identifier("a")])
```

Get's converted into:

```
0 OP_CONSTANT cons->[1] | tnsr->10
2 OP_DEFINE_GLOBAL cons->[0] | intr->a
4 OP_GET_GLOBAL cons->[2] | intr->a
6 OP_PRINT
7 OP_RETURN
```

[Chunk](./src/chunk.rs) - It's stored the bytecode instructions along with any constants like strings, numbers, etc.

### Execution

[vm.rs](./src/vm.rs)

The VM takes the list of bytecode instructions and executes them. It is simply a loop that reads the instructions and executes them.

The VM has a stack-based architecture, which means that the instructions are executed by pushing and popping values from the stack.

For example, the bytecode:

```
0 OP_CONSTANT cons->[1] | tnsr->10
2 OP_DEFINE_GLOBAL cons->[0] | intr->a
4 OP_GET_GLOBAL cons->[2] | intr->a
6 OP_PRINT
7 OP_RETURN
```

Get's executed and prints:

```
10
```

## References

- [Crafting Interpreters](https://craftinginterpreters.com/)
- [Pratt Parsing](https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html)
- [Logos](https://github.com/maciejhirsz/logos)
39 changes: 39 additions & 0 deletions pkg/ai.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/* tslint:disable */
/* eslint-disable */
/**
* @param {string} src
* @returns {string}
*/
export function run_source(src: string): string;

export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;

export interface InitOutput {
readonly memory: WebAssembly.Memory;
readonly run_source: (a: number, b: number, c: number) => void;
readonly __wbindgen_add_to_stack_pointer: (a: number) => number;
readonly __wbindgen_malloc: (a: number, b: number) => number;
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
readonly __wbindgen_free: (a: number, b: number, c: number) => void;
}

export type SyncInitInput = BufferSource | WebAssembly.Module;
/**
* Instantiates the given `module`, which can either be bytes or
* a precompiled `WebAssembly.Module`.
*
* @param {SyncInitInput} module
*
* @returns {InitOutput}
*/
export function initSync(module: SyncInitInput): InitOutput;

/**
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
* for everything else, calls `WebAssembly.instantiate` directly.
*
* @param {InitInput | Promise<InitInput>} module_or_path
*
* @returns {Promise<InitOutput>}
*/
export default function __wbg_init (module_or_path?: InitInput | Promise<InitInput>): Promise<InitOutput>;
Loading

0 comments on commit c00f584

Please sign in to comment.