Skip to content

Commit

Permalink
Merge branch 'main' into breakpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
arnav-ag committed Apr 15, 2024
2 parents ee1cf0f + e9bfadd commit 202f2ff
Show file tree
Hide file tree
Showing 8 changed files with 285 additions and 33 deletions.
16 changes: 16 additions & 0 deletions src/parser/ooga.pegjs
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,14 @@ SequenceStatement
};
}

TypeWithoutInit
= type:(StructIdentifier / InitType)? {
return {
type: type || null,
init: null
};
}

TypeWithInit
= type:(StructIdentifier / InitType)? init:(__ (StructInitializer / Initialiser)) {
return {
Expand All @@ -690,6 +698,14 @@ VariableStatement
type: typeInit.type || "Unknown"
}
}
/ VarToken __ id:Identifier __ typeInit:(TypeWithoutInit)? EOS {
return {
tag: "VariableDeclaration",
id: id,
expression: null,
type: typeInit.type || "Unknown"
}
}
/ id:Identifier init:(__ ShorthandInitialiser) EOS {
return {
tag: "VariableDeclaration",
Expand Down
99 changes: 99 additions & 0 deletions src/tests/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1768,3 +1768,102 @@ x[6]; // still garbage data
'',
defaultNumWords
);

// Test the standard library mutex. It forces the thread that fails to lock to yield and pass on control
// to the next thread. It also demonstrates that the WaitGroup works as intended.
testProgram(
`
var x int = 0;
wg := WaitGroup{10};
func goo(i int, m Mutex) {
print(i + " just started");
m.Lock();
print(i + " is locking");
yieldThread(); // immediately give up again
print(i + " is incrementing");
x = x + 5;
m.Unlock();
wg.Done();
}
m := NewMutex();
for i := 0; i < 10; i++ {
go goo(i, m);
}
wg.Wait();
print(x);
`,
50,
'"0 just started"\n' +
'"0 is locking"\n' +
'"1 just started"\n' +
'"0 is incrementing"\n' +
'"1 is locking"\n' +
'"2 just started"\n' +
'"1 is incrementing"\n' +
'"2 is locking"\n' +
'"3 just started"\n' +
'"2 is incrementing"\n' +
'"3 is locking"\n' +
'"4 just started"\n' +
'"3 is incrementing"\n' +
'"4 is locking"\n' +
'"5 just started"\n' +
'"4 is incrementing"\n' +
'"5 is locking"\n' +
'"6 just started"\n' +
'"5 is incrementing"\n' +
'"6 is locking"\n' +
'"7 just started"\n' +
'"6 is incrementing"\n' +
'"7 is locking"\n' +
'"8 just started"\n' +
'"7 is incrementing"\n' +
'"8 is locking"\n' +
'"9 just started"\n' +
'"8 is incrementing"\n' +
'"9 is locking"\n' +
'"9 is incrementing"\n50',
defaultNumWords
);

// test default initialization
testProgram(
`
type Vector struct {
x int
y int
z Vector
}
var x int; // defaults to 0
print(x); // 0
var y bool;
print(y); // false
var z string;
print(z); // ""
var a Vector;
var b Vector = Vector{1, 2, Vector{3, 4, nil} };
print(a.x); // 0
print(a.y); // 0
print(a.z); // "nil"
print(b.x); // 1
print(b.y); // 2
print(b.z); // "<struct>"
print(nil); // "nil"
print(a.z == nil); // true
5;
`,
5,
'0\nfalse\n""\n0\n0\n"nil"\n1\n2\n"<struct>"\n"nil"\ntrue',
defaultNumWords
);
91 changes: 89 additions & 2 deletions src/vm/oogavm-compiler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
import Opcodes from './opcodes.js';
import { builtinMappings, initializeBuiltinTable } from './oogavm-machine.js';
import debug from 'debug';
import { ArrayType, ChanType, is_type, StructType, Type } from './oogavm-types.js';
import {
ArrayType,
BooleanType,
ChanType,
FloatType,
IntegerType,
is_type,
NullType,
StringType,
StructType,
Type,
} from './oogavm-types.js';
import assert from 'assert';
import { CompilerError, OogaError } from './oogavm-errors.js';
import { unparse } from '../utils/utils.js';
Expand Down Expand Up @@ -173,6 +184,69 @@ function scanForLocalsSingle(comp): CompileTimeVariable[] {
}
}

function defaultInitializeStruct(ce: CompileTimeEnvironment, type: StructType) {
let instr = {
tag: 'StructInitializer',
fields: [],
named: false,
type: type,
};
for (let field of type.fields) {
let defaultValue;
let tag = field.type.name;
let type = { name: field.type.name };
if (is_type(field.type, IntegerType)) {
defaultValue = 0;
} else if (is_type(field.type, FloatType)) {
defaultValue = 0;
} else if (is_type(field.type, BooleanType)) {
defaultValue = false;
} else if (is_type(field.type, StringType)) {
defaultValue = '';
} else {
defaultValue = null;
tag = 'Null';
type = { name: 'Null' };
}
instr.fields.push({
tag: tag,
value: defaultValue,
type: type,
});
}
log('Created fake instr');
log(instr);
compile(instr, ce);
}

// Helper function to default initialize a type
function defaultInitialize(ce: CompileTimeEnvironment, type: Type) {
log('Default initialize');
log(type);
if (is_type(type, IntegerType)) {
log('Default integer');
instrs[wc++] = { tag: Opcodes.LDCI, val: 0 };
} else if (is_type(type, FloatType)) {
log('Default Float');
instrs[wc++] = { tag: Opcodes.LDCI, val: 0 };
} else if (is_type(type, BooleanType)) {
instrs[wc++] = { tag: Opcodes.LDBI, val: false };
} else if (is_type(type, StringType)) {
instrs[wc++] = { tag: Opcodes.LDCS, val: '' };
} else if (is_type(type, StructType)) {
// If this is a struct, convert the null expression into a StructInitializer compile instruction
// with default values
let sType = type as StructType;
defaultInitializeStruct(ce, sType);
} else if (is_type(type, ArrayType)) {
instrs[wc++] = { tag: Opcodes.LDN };
} else if (is_type(type, ChanType)) {
instrs[wc++] = { tag: Opcodes.LDN };
} else {
throw new CompilerError('Unsupported type for default initialization');
}
}

// ******************
// Compilation
// ******************
Expand Down Expand Up @@ -208,7 +282,7 @@ const compileComp = {
instrs[wc++] = { tag: Opcodes.LDBI, val: comp.value };
},
Null: (comp, ce) => {
instrs[wc++] = { tag: Opcodes.LDCI, val: comp.value };
instrs[wc++] = { tag: Opcodes.LDN };
},
String: (comp, ce) => {
instrs[wc++] = { tag: Opcodes.LDCS, val: comp.value };
Expand Down Expand Up @@ -366,8 +440,14 @@ const compileComp = {
},
VariableDeclaration: (comp, ce) => {
// Process the expression as before
log('Variable Declaration for ');
log(comp);
if (comp.expression !== null) {
compile(comp.expression, ce);
} else {
// do default initialization here
// If the expression is null, the type is guaranteed not to be null by parser
defaultInitialize(ce, comp.type);
}
instrs[wc++] = {
tag: Opcodes.ASSIGN,
Expand Down Expand Up @@ -474,6 +554,13 @@ const compileComp = {
compile(comp.id, ce);
log('UpdateExpression: comp ', unparse(comp));
instrs[wc++] = { tag: Opcodes.UNARY, operator: comp.operator };
// Check if left hand side is a const
const variable = getCompileTimeVariable(ce, comp.id.name);

if (variable && variable.is_const) {
throw new CompilerError('Cannot reassign constant ' + comp.id.name);
}

if (comp.id.tag === 'Name') {
instrs[wc++] = {
tag: Opcodes.ASSIGN,
Expand Down
35 changes: 9 additions & 26 deletions src/vm/oogavm-heap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,7 @@ export enum Tag {
STACK,
BUILTIN,
STRUCT,
STRUCT_FIELD,
STRING,
MUTEX,
ARRAY,
SLICE,
BUFFERED,
Expand Down Expand Up @@ -72,8 +70,6 @@ function getTagString(tag: Tag): string {
return 'STRING';
case Tag.STRUCT:
return 'STRUCT';
case Tag.MUTEX:
return 'MUTEX';
case Tag.ARRAY:
return 'ARRAY';
case Tag.SLICE:
Expand Down Expand Up @@ -225,7 +221,7 @@ function isBoolean(address: number): boolean {
return isTrue(address) || isFalse(address);
}

function isNull(address: number): boolean {
export function isNull(address: number): boolean {
return getTag(address) === Tag.NULL;
}

Expand Down Expand Up @@ -549,20 +545,6 @@ function isString(address: number): boolean {
return getTag(address) === Tag.STRING;
}

// ********************************
// Mutex
// ********************************
// 1st word: tag and size
// 2nd word: forwarding address
export function allocateMutex() {
const mutexAddress = allocate(Tag.MUTEX, headerSize);
return mutexAddress;
}

export function isMutex(address: number): boolean {
return getTag(address) === Tag.MUTEX;
}

// ********************************
// Array
// ********************************
Expand Down Expand Up @@ -1130,19 +1112,20 @@ export function addressToTSValue(address: number) {
}

export function TSValueToAddress(value: any) {
if (typeof value === 'string') {
return allocateString(value);
} else if (typeof value === 'boolean') {
return value ? True : False;
} else if (typeof value === 'number') {
return allocateNumber(value);
} else if (typeof value === 'undefined') {
if (typeof value === 'undefined') {
return Undefined;
} else if (value === null) {
// it already went past the undefined check, so this will only
// return true for null
// https://stackoverflow.com/questions/28975896/is-there-a-way-to-check-for-both-null-and-undefined
return Null;
}
if (typeof value === 'string') {
return allocateString(value);
} else if (typeof value === 'boolean') {
return value ? True : False;
} else if (typeof value === 'number') {
return allocateNumber(value);
} else {
throw new Error('not implemented yet, value: ' + JSON.stringify(value, null, 2));
}
Expand Down
Loading

0 comments on commit 202f2ff

Please sign in to comment.