Skip to content

Commit

Permalink
Add default initialization
Browse files Browse the repository at this point in the history
Also fix a bug with how Nil was being treated by the machine, which would cause it to be passed around as the number 4
  • Loading branch information
JothamWong committed Apr 15, 2024
1 parent f380526 commit 001d25c
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 12 deletions.
36 changes: 35 additions & 1 deletion src/tests/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1723,7 +1723,7 @@ for i := 0; i < len(vs); i++ {
print(vs[i]); // null 5 times
}
10;
`, 10, 'null\nnull\nnull\nnull\nnull\n', defaultNumWords);
`, 10, '"nil"\n"nil"\n"nil"\n"nil"\n"nil"\n', defaultNumWords);


// Test out of bounds error
Expand Down Expand Up @@ -1793,3 +1793,37 @@ print(x);
'"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);
89 changes: 87 additions & 2 deletions src/vm/oogavm-compiler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
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 +183,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 +281,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 +439,13 @@ 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 +552,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
17 changes: 9 additions & 8 deletions src/vm/oogavm-heap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,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 @@ -975,19 +975,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
11 changes: 10 additions & 1 deletion src/vm/oogavm-machine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ import {
isChannel,
isClosure, isSlice,
isUnassigned,
isNull,
isUnbufferedChannel,
isUnbufferedChannelEmpty,
isUnbufferedChannelFull,
isUnbufferedChannelFull, Null,
peekStack,
peekStackN,
popBufferedChannel,
Expand Down Expand Up @@ -242,6 +243,11 @@ export const builtinMappings = {
let value: any;
log('print sys call');
[OS[0], value] = popStack(OS[0]);
// need to handle the string representation of nil differently
if (isNull(value)) {
console.log("nil");
return value;
}
console.log(addressToTSValue(value));
return value;
},
Expand Down Expand Up @@ -433,6 +439,9 @@ const microcode = {
LDCS: instr => {
pushTSValueOS(instr.val);
},
LDN: instr => {
pushAddressOS([Null]);
},
LDARR: instr => {
// This instruction loads an array literal
const arity = instr.arity;
Expand Down
9 changes: 9 additions & 0 deletions src/vm/oogavm-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,15 @@ export function equal_type(ts1: Type, ts2: Type, cache = new Set<string>()): boo
return result;
}

// Handle either nil
if (ts1 instanceof StructType && ts2 instanceof NullType) {
return true;
}

if (ts1 instanceof NullType && ts2 instanceof StructType) {
return true;
}

if (ts1 instanceof StructType && ts2 instanceof StructType) {
log('Comparing struct types', ts1, ts2);
if (ts1.structName !== ts2.structName) {
Expand Down

0 comments on commit 001d25c

Please sign in to comment.