Skip to content

Commit

Permalink
Merge branch 'main' into typechecker
Browse files Browse the repository at this point in the history
  • Loading branch information
arnav-ag committed Apr 7, 2024
2 parents ead0b6a + 1996e95 commit b1a49db
Show file tree
Hide file tree
Showing 4 changed files with 331 additions and 211 deletions.
119 changes: 96 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,95 @@
# Ooga

Ooga is a VM-based sub-language of Go developed using TypeScript.
It includes the Ooga toolchain and a web-based playground website using Microsoft's Monaco
editor for execution of code.

## Quick Start

Set up instructions here.

## Features

### Sequential Programming Constructs

- Expressions
- Conditionals
- For loops
- Block Scoping
- Function Declarations
- Variable Declarations
- Constant Declarations
- Structs

### Concurrent Programming Constructs

- Goroutines
- WaitGroups

Behind the scenes, Ooga uses a Round Robin scheduler to provide "concurrency" and allows for users
to construct race conditions.

### Garbage Collection

Ooga uses the LISP 2 Garbage Collection algorithm and supports an unlimited number of declarations (up
to available memory), with no arbitrary node size restriction. Programs such as

```go
func OOGA() int {
var a int = 1;
var b int = 2;
var c int = 3;
var d int = 4;
var e int = 5;
var f int = 6;
var g int = 7;
var h int = 8;
var i int = 9;
var j int = 10;
var k int = 11;
var l int = 12;
var m int = 13;
var n int = 14;
var o int = 15;
var p int = 16;
var q int = 17;
var r int = 18;
var s int = 19;
var t int = 20;
var u int = 21;
var v int = 22;
var w int = 23;
var x int = 24;
var y int = 25;
var z int = 26;
return a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u + v + w +x + y + z;
}
```

### Type Checker

Ooga is a strongly statically typed language that comes with a type checker for ensuring the type safety
of programs.

### Types

Ooga supports integers, floats, booleans, strings and custom Struct expressions.

WORK IN PROGRESS: Slices and Channels

### Test Suite

Ooga comes with a comprehensive test suite that tests all included features. Run `yarn tooga` to see the tests.

### Memory Visualization

Ooga uses a low-level memory model and comes with three built-in functions that lets you visualize
the contents of the heap-based memory model.

The Operating Stack, Runtime Stack and the Environment can be visualized using their corresponding
helper functions.


## Plan

Implementing Type checking
Expand Down Expand Up @@ -29,28 +119,11 @@ A convenient function that does compile and run is `yarn booga`.

## Structure

`oogavm-compiler`: in charge of compiling `*.ooga` files to `*.bm` files.
`oogavm-assembler`: tbh idk why i put this file, got inspired by martin
`oogavm-compiler`: in charge of compiling `*.ooga` files to `*.bm` files.
`oogavm-errors`: ooga errors to distinguish user error from typescript errors.
`oogavm-heap`: low level memory implementation.
`oogavm-machine`: run the `*.bm` file.

## TODOs

1. Think at high level how we want to implement all 3 stretch features.
2. The basic features can be directly mapped from the existing HW and really don't require that much IQ

Some differences from the Homework VM that I know of
1. we do not start from the first line
2. we need to spawn goroutines, therefore, each thread needs its own copies of OS, E and so on
3. I am thinking that we use oogavm-scheduler as in martin, with the main thread spawning a copy of itself for goroutines
4. we would need to handle the goroutine case of binding to heap for closure (see cs3211)
5. we need proper error handling and type system as in golang, return errors???


## Concurrency

To handle concurrency, each


## Parser

Currently do not support nested functions (inside a {} block)
`oogavm-scheduler`: process scheduler.
`oogavm-typechecker`: ooga's typechecker.
`opcodes`: enumeration of machine opcodes.
34 changes: 34 additions & 0 deletions src/tests/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -890,6 +890,23 @@ a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t +
defaultNumWords
);

// Test re-allocation GC
testProgram(
`
var x int = 5;
var y int = 10;
var z int = 15;
func googa(x int, y int, z int) int {
return x + y + z;
}
googa(x, y, z);
`,
30,
104
);

// Negative test for more than once variable declaration
testProgram(
`
Expand Down Expand Up @@ -917,3 +934,20 @@ var x int = 5;
// }
// print(x + " " + y);
// `, "Jotham Wong", 104);

// Test GC with NEW_THREAD instruction to make sure everything works
testProgram(
`
var x int = 5;
var y int = 10;
func googa(x int, y int) int {
print(x + y);
return x + y;
}
go googa(x, y);
`,
true,
104
);
56 changes: 37 additions & 19 deletions src/vm/oogavm-heap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ function getTagString(tag: Tag): string {
return 'BUILTIN';
case Tag.STRING:
return 'STRING';
case Tag.STRUCT:
return 'STRUCT';
default:
return 'UNKNOWN';
}
Expand Down Expand Up @@ -245,10 +247,10 @@ export function getPrevStackAddress(address: number): number {
return getWordOffset(address, prevStackElementOffset);
}

export function pushStack(stackAddress: number, stackElementAddress: number): number {
export function pushStack(stackAddress: number[], stackElementAddress: number[]): number {
const nextStack = allocate(Tag.STACK, 4);
setWord(nextStack + prevStackElementOffset, stackAddress);
setWord(nextStack + stackEntryOffset, stackElementAddress);
setWord(nextStack + prevStackElementOffset, stackAddress[0]);
setWord(nextStack + stackEntryOffset, stackElementAddress[0]);
return nextStack;
}

Expand Down Expand Up @@ -285,11 +287,11 @@ const closureArityOffset = 1;
const closurePcOffset = 3;
// 3 words
const closureEnvOffset = 3;
export function allocateClosure(arity: number, pc: number, envAddress: number): number {
export function allocateClosure(arity: number, pc: number, envAddress: number[]): number {
const address = allocate(Tag.CLOSURE, 4);
setByteAtOffset(address + headerSize, closureArityOffset, arity);
set2ByteAtOffset(address + headerSize, closurePcOffset, pc);
setWord(address + closureEnvOffset, envAddress);
setWord(address + closureEnvOffset, envAddress[0]);
return address;
}

Expand All @@ -315,10 +317,10 @@ export function isClosure(address: number): boolean {
// 3rd word is blockFrameEnv
const blockFrameEnvOffset = 2;

export function allocateBlockFrame(envAddress: number): number {
export function allocateBlockFrame(envAddress: number[]): number {
const address = allocate(Tag.BLOCKFRAME, 3);
log('BlockFrame at addr ' + address + ' will point to E=' + envAddress);
setWord(address + blockFrameEnvOffset, envAddress);
log('BlockFrame at addr ' + address + ' will point to E=' + envAddress[0]);
setWord(address + blockFrameEnvOffset, envAddress[0]);
return address;
}

Expand All @@ -339,10 +341,10 @@ const callFramePCOffset = 2;
// 4th word
const callFrameEnvAddress = 3;

export function allocateCallFrame(envAddress: number, pc: number): number {
export function allocateCallFrame(envAddress: number[], pc: number): number {
const address = allocate(Tag.CALLFRAME, 4);
setWord(address + callFramePCOffset, pc);
setWord(address + callFrameEnvAddress, envAddress);
setWord(address + callFrameEnvAddress, envAddress[0]);
return address;
}

Expand Down Expand Up @@ -393,14 +395,14 @@ export function setFrameValue(frameAddress: number, i: number, value: number) {
// creates a copy of an environment that is bigger by 1 frame slot than the previous env
// and copies the frame addresses of the given env to the new env
// then sets the address of te new frame to the end of the new env
export function extendEnvironment(frameAddress: number, envAddress: number): number {
const oldSize = getSize(envAddress) - headerSize;
export function extendEnvironment(frameAddress: number[], envAddress: number[]): number {
const oldSize = getSize(envAddress[0]) - headerSize;
const newEnvAddress = allocateEnvironment(oldSize + 1);
let i = 0;
for (i = 0; i < oldSize; i++) {
setWord(newEnvAddress + i + headerSize, getWordOffset(envAddress, i + headerSize));
setWord(newEnvAddress + i + headerSize, getWordOffset(envAddress[0], i + headerSize));
}
setWord(newEnvAddress + i + headerSize, frameAddress);
setWord(newEnvAddress + i + headerSize, frameAddress[0]);
return newEnvAddress;
}

Expand Down Expand Up @@ -574,7 +576,9 @@ export function debugHeap(): void {
}
break;
case Tag.STRUCT:
// TODO
for (let i = 0; i < getSize(curr) - headerSize; i++) {
log('Field ' + i + ': ' + getWordOffset(curr, i + headerSize));
}
break;
case Tag.CALLFRAME:
log('PC: ' + getCallFramePC(curr));
Expand Down Expand Up @@ -746,7 +750,18 @@ function computeForwardingAddresses() {
// to the current freePtr and increment the freePtr according to
// the object's size.
if (isMarked(livePtr)) {
const originalTag = -1 - getTag(livePtr);
log(
'Forwarding ' +
livePtr +
' of <' +
getTagString(originalTag) +
'>' +
' to ' +
freePtr
);
setForwardingAddress(livePtr, freePtr);
rootMappings.set(livePtr, freePtr);
freePtr += size;
}
livePtr += size;
Expand Down Expand Up @@ -841,12 +856,16 @@ function moveLiveObjects() {
free = freePtr;
}

let roots: number[][] = [];
let rootMappings = new Map<number, number>();

function collectGarbage() {
// First pass: marking
log('Collecting da garbage...');
let roots = getRoots();
roots = getRoots();
rootMappings.clear();
for (let root of roots) {
mark(root);
mark(root[0]);
}
for (let literal of literals) {
mark(literal);
Expand All @@ -859,15 +878,14 @@ function collectGarbage() {
log(sKey + ' -> ' + StringPool.get(sKey));
mark(StringPool.get(sKey));
}

// Second pass: Compute forwarding location for live objects
computeForwardingAddresses();
// Third pass: update all pointers to the forwarding addresses
updateReferences();
// we need to update the roots first because if the root is at a place
// after free, the forwarding address there is basically garbage data
// can't avoid this tight coupling unfortunately
updateRoots(getForwardingAddress(E), getForwardingAddress(OS), getForwardingAddress(RTS));
updateRoots(roots, rootMappings);
// Final pass: move all live objects to their forwarding address
moveLiveObjects();
}
Loading

0 comments on commit b1a49db

Please sign in to comment.